ccstatusline-usage 2.0.46 → 2.1.1
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/README.md +23 -1
- package/dist/ccstatusline.js +100 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,12 +33,20 @@ This fork adds API-based usage widgets beyond the upstream:
|
|
|
33
33
|
- **Reset Timer** - Time until 5-hour session window resets
|
|
34
34
|
- **Context Window Display** - Visual bar showing context usage
|
|
35
35
|
- **Two-line Layout** - Session info on line 1, context on line 2
|
|
36
|
+
- **Narrow Terminal Detection** - Auto-compact rendering on terminals < 80 cols via tmux pane width detection
|
|
36
37
|
|
|
37
38
|
### Enhanced Status Line Preview
|
|
38
39
|
|
|
40
|
+
**Desktop (full width):**
|
|
39
41
|
```
|
|
40
42
|
Session: [████░░░░░░░░░░░] 27.0% | Weekly: [████████████░░░] 86.0% | 1:56 hr | Extra: $41.24/$47.00 | Model: Opus 4.6 | Session ID: 714aa815-8a...
|
|
41
|
-
|
|
43
|
+
Context: [███████░░░░░░░░] 103k/200k (51%)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Narrow terminal (< 80 cols, e.g. mobile SSH via tmux):**
|
|
47
|
+
```
|
|
48
|
+
S: [██░░] 51.0% | W: [████] 92.0% | 0:00 hr
|
|
49
|
+
C: [██░░] 100k/200k
|
|
42
50
|
```
|
|
43
51
|
|
|
44
52
|
These widgets are enabled by default. Just install and run!
|
|
@@ -65,6 +73,19 @@ These widgets are enabled by default. Just install and run!
|
|
|
65
73
|
|
|
66
74
|
## 🆕 Recent Updates
|
|
67
75
|
|
|
76
|
+
### [v2.1.1](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.1) - Automatic mobile/narrow terminal detection
|
|
77
|
+
|
|
78
|
+
- **Automatic compact rendering** on terminals < 80 columns via tmux pane width detection:
|
|
79
|
+
```
|
|
80
|
+
S: [██░░] 51.0% | W: [████] 92.0% | 0:00 hr
|
|
81
|
+
C: [██░░] 100k/200k
|
|
82
|
+
```
|
|
83
|
+
- Widgets hidden on narrow terminals: Model, Session Clock, Session ID
|
|
84
|
+
- Progress bars shrink from 15 to 4 characters, labels abbreviated (Session → S, Weekly → W, Context → C)
|
|
85
|
+
- Orphaned separators automatically removed when adjacent widgets are hidden
|
|
86
|
+
- FlexMode `-40` override on narrow terminals (no auto-compact sidebar)
|
|
87
|
+
- Works with any tmux-based remote access setup (SSH clients, mobile terminals, etc.)
|
|
88
|
+
|
|
68
89
|
### [v2.0.46](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.0.46) - Upstream sync
|
|
69
90
|
|
|
70
91
|
- Merged 101 upstream commits: line reorder move mode, Claude Session ID widget, `CLAUDE_CONFIG_DIR` support, Windows fixes, Block Timer fixes, model string/object handling fix, and more
|
|
@@ -163,6 +184,7 @@ These widgets are enabled by default. Just install and run!
|
|
|
163
184
|
- **⚙️ Global Options** - Apply consistent formatting across all widgets (padding, separators, bold, background)
|
|
164
185
|
- **🚀 Cross-platform** - Works seamlessly with both Bun and Node.js
|
|
165
186
|
- **🔧 Flexible Configuration** - Supports custom Claude Code config directory via `CLAUDE_CONFIG_DIR` environment variable
|
|
187
|
+
- **📱 Narrow Terminal Detection** - Auto-compact rendering on terminals < 80 columns via tmux pane width detection
|
|
166
188
|
- **📏 Smart Width Detection** - Automatically adapts to terminal width with flex separators
|
|
167
189
|
- **⚡ Zero Config** - Sensible defaults that work out of the box
|
|
168
190
|
|
package/dist/ccstatusline.js
CHANGED
|
@@ -51450,7 +51450,8 @@ import { execSync as execSync3 } from "child_process";
|
|
|
51450
51450
|
import * as fs5 from "fs";
|
|
51451
51451
|
import * as path4 from "path";
|
|
51452
51452
|
var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils";
|
|
51453
|
-
var
|
|
51453
|
+
var MOBILE_THRESHOLD = 80;
|
|
51454
|
+
var PACKAGE_VERSION = "2.1.1";
|
|
51454
51455
|
function getPackageVersion() {
|
|
51455
51456
|
if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
|
|
51456
51457
|
return PACKAGE_VERSION;
|
|
@@ -51530,6 +51531,60 @@ function canDetectTerminalWidth() {
|
|
|
51530
51531
|
return false;
|
|
51531
51532
|
}
|
|
51532
51533
|
}
|
|
51534
|
+
function parseTmuxClients() {
|
|
51535
|
+
try {
|
|
51536
|
+
const output = execSync3("tmux list-clients -F '#{client_width} #{client_height} #{client_tty}'", { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 2000 }).trim();
|
|
51537
|
+
if (!output)
|
|
51538
|
+
return [];
|
|
51539
|
+
const clients = [];
|
|
51540
|
+
for (const line of output.split(`
|
|
51541
|
+
`)) {
|
|
51542
|
+
const parts = line.trim().split(" ");
|
|
51543
|
+
if (parts.length >= 3) {
|
|
51544
|
+
const width = parseInt(parts[0] ?? "", 10);
|
|
51545
|
+
const height = parseInt(parts[1] ?? "", 10);
|
|
51546
|
+
const tty2 = parts.slice(2).join(" ");
|
|
51547
|
+
if (!isNaN(width) && !isNaN(height) && tty2) {
|
|
51548
|
+
clients.push({ width, height, tty: tty2 });
|
|
51549
|
+
}
|
|
51550
|
+
}
|
|
51551
|
+
}
|
|
51552
|
+
return clients;
|
|
51553
|
+
} catch {
|
|
51554
|
+
return [];
|
|
51555
|
+
}
|
|
51556
|
+
}
|
|
51557
|
+
function getTmuxPaneWidth() {
|
|
51558
|
+
try {
|
|
51559
|
+
const output = execSync3("tmux display-message -p '#{pane_width}'", { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 2000 }).trim();
|
|
51560
|
+
const parsed = parseInt(output, 10);
|
|
51561
|
+
if (!isNaN(parsed) && parsed > 0)
|
|
51562
|
+
return parsed;
|
|
51563
|
+
} catch {}
|
|
51564
|
+
return null;
|
|
51565
|
+
}
|
|
51566
|
+
function detectTerminalEnvironment() {
|
|
51567
|
+
const inTmux = Boolean(process.env.TMUX);
|
|
51568
|
+
let terminalWidth = getTerminalWidth();
|
|
51569
|
+
let smallestClientWidth = null;
|
|
51570
|
+
let tmuxClients = [];
|
|
51571
|
+
if (inTmux) {
|
|
51572
|
+
const paneWidth = getTmuxPaneWidth();
|
|
51573
|
+
if (paneWidth !== null)
|
|
51574
|
+
terminalWidth = paneWidth;
|
|
51575
|
+
tmuxClients = parseTmuxClients();
|
|
51576
|
+
if (tmuxClients.length > 0)
|
|
51577
|
+
smallestClientWidth = Math.min(...tmuxClients.map((c) => c.width));
|
|
51578
|
+
}
|
|
51579
|
+
const isMobile = terminalWidth !== null && terminalWidth < MOBILE_THRESHOLD;
|
|
51580
|
+
return {
|
|
51581
|
+
inTmux,
|
|
51582
|
+
tmuxClients,
|
|
51583
|
+
terminalWidth,
|
|
51584
|
+
isMobile,
|
|
51585
|
+
smallestClientWidth
|
|
51586
|
+
};
|
|
51587
|
+
}
|
|
51533
51588
|
|
|
51534
51589
|
// node_modules/ink-select-input/build/Indicator.js
|
|
51535
51590
|
var import_react27 = __toESM(require_react(), 1);
|
|
@@ -52297,6 +52352,8 @@ class ModelWidget {
|
|
|
52297
52352
|
if (context.isPreview) {
|
|
52298
52353
|
return item.rawValue ? "Claude" : "Model: Claude";
|
|
52299
52354
|
}
|
|
52355
|
+
if (context.terminalEnv?.isMobile)
|
|
52356
|
+
return null;
|
|
52300
52357
|
const model = context.data?.model;
|
|
52301
52358
|
const modelDisplayName = typeof model === "string" ? model : model?.display_name ?? model?.id;
|
|
52302
52359
|
if (modelDisplayName) {
|
|
@@ -52644,6 +52701,8 @@ function renderPowerlineStatusLine(widgets, settings, context, lineIndex = 0, gl
|
|
|
52644
52701
|
} else if (flexMode === "full-until-compact") {
|
|
52645
52702
|
terminalWidth = detectedWidth - 6;
|
|
52646
52703
|
}
|
|
52704
|
+
} else if (context.terminalEnv?.isMobile) {
|
|
52705
|
+
terminalWidth = detectedWidth - 6;
|
|
52647
52706
|
} else {
|
|
52648
52707
|
if (flexMode === "full") {
|
|
52649
52708
|
terminalWidth = detectedWidth - 6;
|
|
@@ -53018,6 +53077,8 @@ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCal
|
|
|
53018
53077
|
} else if (flexMode === "full-until-compact") {
|
|
53019
53078
|
terminalWidth = detectedWidth - 6;
|
|
53020
53079
|
}
|
|
53080
|
+
} else if (context.terminalEnv?.isMobile) {
|
|
53081
|
+
terminalWidth = detectedWidth - 6;
|
|
53021
53082
|
} else {
|
|
53022
53083
|
if (flexMode === "full") {
|
|
53023
53084
|
terminalWidth = detectedWidth - 6;
|
|
@@ -53053,6 +53114,18 @@ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCal
|
|
|
53053
53114
|
}
|
|
53054
53115
|
if (!hasContentBefore)
|
|
53055
53116
|
continue;
|
|
53117
|
+
let hasContentAfter = false;
|
|
53118
|
+
for (let j = i + 1;j < widgets.length; j++) {
|
|
53119
|
+
const nextWidget = widgets[j];
|
|
53120
|
+
if (nextWidget && nextWidget.type !== "separator" && nextWidget.type !== "flex-separator") {
|
|
53121
|
+
if (preRenderedWidgets[j]?.content) {
|
|
53122
|
+
hasContentAfter = true;
|
|
53123
|
+
break;
|
|
53124
|
+
}
|
|
53125
|
+
}
|
|
53126
|
+
}
|
|
53127
|
+
if (!hasContentAfter)
|
|
53128
|
+
continue;
|
|
53056
53129
|
const sepChar = widget.character ?? (settings.defaultSeparator ?? "|");
|
|
53057
53130
|
const formattedSep = formatSeparator(sepChar);
|
|
53058
53131
|
let separatorColor = widget.color ?? "gray";
|
|
@@ -53563,6 +53636,8 @@ class SessionClockWidget {
|
|
|
53563
53636
|
if (context.isPreview) {
|
|
53564
53637
|
return item.rawValue ? "2hr 15m" : "Session: 2hr 15m";
|
|
53565
53638
|
}
|
|
53639
|
+
if (context.terminalEnv?.isMobile)
|
|
53640
|
+
return null;
|
|
53566
53641
|
const duration3 = context.sessionDuration ?? "0m";
|
|
53567
53642
|
return item.rawValue ? duration3 : `Session: ${duration3}`;
|
|
53568
53643
|
}
|
|
@@ -54388,13 +54463,13 @@ class ClaudeSessionIdWidget {
|
|
|
54388
54463
|
render(item, context, settings) {
|
|
54389
54464
|
if (context.isPreview) {
|
|
54390
54465
|
return item.rawValue ? "preview-session-id" : "Session ID: preview-session-id";
|
|
54391
|
-
} else {
|
|
54392
|
-
const sessionId = context.data?.session_id;
|
|
54393
|
-
if (!sessionId) {
|
|
54394
|
-
return null;
|
|
54395
|
-
}
|
|
54396
|
-
return item.rawValue ? sessionId : `Session ID: ${sessionId}`;
|
|
54397
54466
|
}
|
|
54467
|
+
if (context.terminalEnv?.isMobile)
|
|
54468
|
+
return null;
|
|
54469
|
+
const sessionId = context.data?.session_id;
|
|
54470
|
+
if (!sessionId)
|
|
54471
|
+
return null;
|
|
54472
|
+
return item.rawValue ? sessionId : `Session ID: ${sessionId}`;
|
|
54398
54473
|
}
|
|
54399
54474
|
supportsRawValue() {
|
|
54400
54475
|
return true;
|
|
@@ -54593,11 +54668,17 @@ function getErrorMessage(error43) {
|
|
|
54593
54668
|
return "[Parse Error]";
|
|
54594
54669
|
}
|
|
54595
54670
|
}
|
|
54596
|
-
|
|
54671
|
+
var MOBILE_BAR_WIDTH = 4;
|
|
54672
|
+
var DEFAULT_BAR_WIDTH = 15;
|
|
54673
|
+
function makeProgressBar(percent, width = DEFAULT_BAR_WIDTH) {
|
|
54597
54674
|
const filled = Math.round(percent / 100 * width);
|
|
54598
54675
|
const empty = width - filled;
|
|
54599
54676
|
return "[" + "█".repeat(filled) + "░".repeat(empty) + "]";
|
|
54600
54677
|
}
|
|
54678
|
+
function formatUsageBar(label, shortLabel, percent, mobile) {
|
|
54679
|
+
const bar = makeProgressBar(percent, mobile ? MOBILE_BAR_WIDTH : DEFAULT_BAR_WIDTH);
|
|
54680
|
+
return `${mobile ? shortLabel : label}: ${bar} ${percent.toFixed(1)}%`;
|
|
54681
|
+
}
|
|
54601
54682
|
|
|
54602
54683
|
class SessionUsageWidget {
|
|
54603
54684
|
getDefaultColor() {
|
|
@@ -54620,8 +54701,7 @@ class SessionUsageWidget {
|
|
|
54620
54701
|
return getErrorMessage(data.error);
|
|
54621
54702
|
if (data.sessionUsage === undefined)
|
|
54622
54703
|
return null;
|
|
54623
|
-
|
|
54624
|
-
return `Session: ${makeProgressBar(percent)} ${percent.toFixed(1)}%`;
|
|
54704
|
+
return formatUsageBar("Session", "S", data.sessionUsage, Boolean(context.terminalEnv?.isMobile));
|
|
54625
54705
|
}
|
|
54626
54706
|
supportsRawValue() {
|
|
54627
54707
|
return false;
|
|
@@ -54652,8 +54732,7 @@ class WeeklyUsageWidget {
|
|
|
54652
54732
|
return getErrorMessage(data.error);
|
|
54653
54733
|
if (data.weeklyUsage === undefined)
|
|
54654
54734
|
return null;
|
|
54655
|
-
|
|
54656
|
-
return `Weekly: ${makeProgressBar(percent)} ${percent.toFixed(1)}%`;
|
|
54735
|
+
return formatUsageBar("Weekly", "W", data.weeklyUsage, Boolean(context.terminalEnv?.isMobile));
|
|
54657
54736
|
}
|
|
54658
54737
|
supportsRawValue() {
|
|
54659
54738
|
return false;
|
|
@@ -54754,7 +54833,11 @@ class ContextBarWidget {
|
|
|
54754
54833
|
const percent = total > 0 ? used / total * 100 : 0;
|
|
54755
54834
|
const usedK = Math.round(used / 1000);
|
|
54756
54835
|
const totalK = Math.round(total / 1000);
|
|
54757
|
-
|
|
54836
|
+
const mobile = Boolean(context.terminalEnv?.isMobile);
|
|
54837
|
+
const bar = makeProgressBar(percent, mobile ? MOBILE_BAR_WIDTH : DEFAULT_BAR_WIDTH);
|
|
54838
|
+
const label = mobile ? "C" : "Context";
|
|
54839
|
+
const suffix = mobile ? "" : ` (${Math.round(percent)}%)`;
|
|
54840
|
+
return `${label}: ${bar} ${usedK}k/${totalK}k${suffix}`;
|
|
54758
54841
|
}
|
|
54759
54842
|
supportsRawValue() {
|
|
54760
54843
|
return false;
|
|
@@ -59729,12 +59812,15 @@ async function renderMultipleLines(data) {
|
|
|
59729
59812
|
if (hasBlockTimer) {
|
|
59730
59813
|
blockMetrics = getBlockMetrics();
|
|
59731
59814
|
}
|
|
59815
|
+
const terminalEnv = detectTerminalEnvironment();
|
|
59732
59816
|
const context = {
|
|
59733
59817
|
data,
|
|
59734
59818
|
tokenMetrics,
|
|
59735
59819
|
sessionDuration,
|
|
59736
59820
|
blockMetrics,
|
|
59737
|
-
|
|
59821
|
+
terminalWidth: terminalEnv.terminalWidth,
|
|
59822
|
+
isPreview: false,
|
|
59823
|
+
terminalEnv
|
|
59738
59824
|
};
|
|
59739
59825
|
const preRenderedLines = preRenderAllWidgets(lines, settings, context);
|
|
59740
59826
|
const preCalculatedMaxWidths = calculateMaxWidthsFromPreRendered(preRenderedLines, settings);
|