ccstatusline-usage 2.3.0 → 2.3.2
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 +16 -1
- package/dist/ccstatusline.js +214 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,15 +31,17 @@
|
|
|
31
31
|
This fork adds API-based usage widgets beyond the upstream:
|
|
32
32
|
|
|
33
33
|
- **Session/Weekly Usage** - Real utilization from Anthropic API with progress bars
|
|
34
|
+
- **Weekly Pace** - Pendulum bar showing if you're ahead or behind expected usage pace
|
|
34
35
|
- **Reset Timer** - Time until 5-hour session window resets
|
|
35
36
|
- **Context Window Display** - Visual bar showing context usage
|
|
37
|
+
- **Off Peak** - Shows peak/off-peak 2x status during Anthropic usage promotions
|
|
36
38
|
- **Two-line Layout** - Session info on line 1, context on line 2
|
|
37
39
|
|
|
38
40
|
### Enhanced Status Line Preview
|
|
39
41
|
|
|
40
42
|
```
|
|
41
43
|
Session: [████░░░░░░░░░░░] 27.0% | Weekly: [███████████████] 100.0% | Extra: €2.50/€50.00 | Model: Opus 4.6 | Session ID: 0109b99d...
|
|
42
|
-
Context: [███████░░░░░░░░] 103k/200k (51%)
|
|
44
|
+
Context: [███████░░░░░░░░] 103k/200k (51%) | Pace: [░░░░░░░|██░░░░░] D5/7 +12% | Off peak: Off-peak 2x
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|

|
|
@@ -64,6 +66,16 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
|
|
|
64
66
|
|
|
65
67
|
## 🆕 Recent Updates
|
|
66
68
|
|
|
69
|
+
### [v2.3.2](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.2) - Off Peak widget + WeeklyPace compact fix
|
|
70
|
+
|
|
71
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Off Peak widget** — New widget showing `Off peak: Off-peak 2x` or `Off peak: Peak` during the Anthropic March 2026 Spring Break promotion (off-peak = weekdays outside 8 AM–2 PM ET, weekends all day). Widget automatically hides after March 28, 2026 23:59 PT.
|
|
72
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **WeeklyPace compact fix** — Pendulum bar now falls back to text mode (`D6/7: -18%`) on narrow terminals (< 80 chars)
|
|
73
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Sonnet [1m] extra usage fix** — Extra usage spending now shown when a charged `[1m]` model (e.g. Sonnet) is active, regardless of weekly limit status
|
|
74
|
+
|
|
75
|
+
### [v2.3.1](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.1) - Weekly Pace widget
|
|
76
|
+
|
|
77
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Weekly Pace widget** — New widget showing if weekly usage pace is on track, with pendulum bar or text display mode (PR #1 by @BenIsLegit)
|
|
78
|
+
|
|
67
79
|
### [v2.3.0](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.0) - Upstream sync + Vim Mode widget
|
|
68
80
|
|
|
69
81
|
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Upstream sync** — Merged 14 upstream commits: Vim Mode widget, hyperlink support for GitBranch/GitRootDir, ESLint 10 upgrade, timer day display fix (≥24h), macOS keychain token fallback, ink-gradient 4.0.0
|
|
@@ -538,12 +550,14 @@ bun run example
|
|
|
538
550
|
- **Battery** *(ccstatusline-usage)* - Shows battery percentage on macOS and Linux (only visible when on battery power, hidden when charging)
|
|
539
551
|
- **Session Usage** - Shows daily/session API usage percentage
|
|
540
552
|
- **Weekly Usage** - Shows weekly API usage percentage
|
|
553
|
+
- **Weekly Pace** - Pendulum bar or text label showing if usage pace is on track, overcooking, or underutilized (toggle with `p` key in TUI)
|
|
541
554
|
- **Block Reset Timer** - Shows time remaining until current 5-hour block reset window
|
|
542
555
|
- **Weekly Reset Timer** - Shows time remaining until weekly usage reset
|
|
543
556
|
- **Context Bar** - Shows context usage as a progress bar with short/full display modes
|
|
544
557
|
- **Skills** - Shows skill activity as last used, total count, or unique list (with optional list limit and hide-when-empty toggle)
|
|
545
558
|
- **Thinking Effort** - Shows the current Claude Code thinking effort level
|
|
546
559
|
- **Vim Mode** - Displays current vim editor mode
|
|
560
|
+
- **Off Peak** *(ccstatusline-usage)* - Shows `Off peak: Off-peak 2x` or `Off peak: Peak` during Anthropic usage promotions (auto-hides when promotion ends)
|
|
547
561
|
- **Separator** - Visual divider between widgets (available when Powerline mode is off and no default separator is configured)
|
|
548
562
|
- **Flex Separator** - Expands to fill available space (available when Powerline mode is off)
|
|
549
563
|
|
|
@@ -631,6 +645,7 @@ Widget-specific shortcuts:
|
|
|
631
645
|
- **Block Timer**: `p` cycle display mode (time/full bar/short bar)
|
|
632
646
|
- **Block Reset Timer**: `p` cycle display mode (time/full bar/short bar)
|
|
633
647
|
- **Weekly Reset Timer**: `p` cycle display mode (time/full bar/short bar)
|
|
648
|
+
- **Weekly Pace**: `p` toggle pendulum bar / text label
|
|
634
649
|
- **Current Working Dir**: `h` home abbreviation, `s` segment editor, `f` fish-style path
|
|
635
650
|
- **Custom Command**: `e` command, `w` max width, `t` timeout, `p` preserve ANSI colors
|
|
636
651
|
- **Link**: `u` URL, `e` link text
|
package/dist/ccstatusline.js
CHANGED
|
@@ -52428,7 +52428,9 @@ var init_Settings = __esm(() => {
|
|
|
52428
52428
|
{ id: "session-id", type: "claude-session-id", color: "cyan" }
|
|
52429
52429
|
],
|
|
52430
52430
|
[
|
|
52431
|
-
{ id: "context-bar", type: "context-bar", color: "blue" }
|
|
52431
|
+
{ id: "context-bar", type: "context-bar", color: "blue" },
|
|
52432
|
+
{ id: "sep-off-peak", type: "separator" },
|
|
52433
|
+
{ id: "off-peak", type: "off-peak", color: "green" }
|
|
52432
52434
|
],
|
|
52433
52435
|
[]
|
|
52434
52436
|
]),
|
|
@@ -54069,7 +54071,7 @@ function getTerminalWidth() {
|
|
|
54069
54071
|
function canDetectTerminalWidth() {
|
|
54070
54072
|
return probeTerminalWidth() !== null;
|
|
54071
54073
|
}
|
|
54072
|
-
var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.
|
|
54074
|
+
var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.2";
|
|
54073
54075
|
var init_terminal = () => {};
|
|
54074
54076
|
|
|
54075
54077
|
// src/utils/renderer.ts
|
|
@@ -60440,6 +60442,19 @@ function resolveUsageWindowWithFallback(usageData, blockMetrics, nowMs = Date.no
|
|
|
60440
60442
|
}
|
|
60441
60443
|
return getUsageWindowFromBlockMetrics(fallbackMetrics, nowMs);
|
|
60442
60444
|
}
|
|
60445
|
+
function getWeeklyUsageWindowFromResetAt(weeklyResetAt, nowMs = Date.now()) {
|
|
60446
|
+
if (!weeklyResetAt) {
|
|
60447
|
+
return null;
|
|
60448
|
+
}
|
|
60449
|
+
const resetAtMs = Date.parse(weeklyResetAt);
|
|
60450
|
+
if (Number.isNaN(resetAtMs)) {
|
|
60451
|
+
return null;
|
|
60452
|
+
}
|
|
60453
|
+
return buildUsageWindow(resetAtMs, nowMs, SEVEN_DAY_WINDOW_MS);
|
|
60454
|
+
}
|
|
60455
|
+
function resolveWeeklyUsageWindow(usageData, nowMs = Date.now()) {
|
|
60456
|
+
return getWeeklyUsageWindowFromResetAt(usageData.weeklyResetAt, nowMs);
|
|
60457
|
+
}
|
|
60443
60458
|
function formatUsageDuration(durationMs, compact2 = false, useDays = true) {
|
|
60444
60459
|
const clampedMs = Math.max(0, durationMs);
|
|
60445
60460
|
const totalHours = Math.floor(clampedMs / (1000 * 60 * 60));
|
|
@@ -60451,6 +60466,28 @@ function formatUsageDuration(durationMs, compact2 = false, useDays = true) {
|
|
|
60451
60466
|
const parts = [d > 0 && `${d}d`, h > 0 && `${h}${hLabel}`, m > 0 && `${m}m`].filter(Boolean);
|
|
60452
60467
|
return parts.length > 0 ? parts.join(sep2) : "0m";
|
|
60453
60468
|
}
|
|
60469
|
+
function getUsageErrorMessage(error48) {
|
|
60470
|
+
switch (error48) {
|
|
60471
|
+
case "no-credentials":
|
|
60472
|
+
return "[No credentials]";
|
|
60473
|
+
case "timeout":
|
|
60474
|
+
return "[Timeout]";
|
|
60475
|
+
case "rate-limited":
|
|
60476
|
+
return "[Rate limited]";
|
|
60477
|
+
case "api-error":
|
|
60478
|
+
return "[API Error]";
|
|
60479
|
+
case "parse-error":
|
|
60480
|
+
return "[Parse Error]";
|
|
60481
|
+
}
|
|
60482
|
+
}
|
|
60483
|
+
function makePendulumBar(delta, halfWidth = 7) {
|
|
60484
|
+
const clamped = Math.max(-100, Math.min(100, delta));
|
|
60485
|
+
const fill2 = Math.min(halfWidth, Math.round(Math.abs(clamped) / 100 * halfWidth));
|
|
60486
|
+
if (clamped < 0) {
|
|
60487
|
+
return "[" + "░".repeat(halfWidth - fill2) + "█".repeat(fill2) + "|" + "░".repeat(halfWidth) + "]";
|
|
60488
|
+
}
|
|
60489
|
+
return "[" + "░".repeat(halfWidth) + "|" + "█".repeat(fill2) + "░".repeat(halfWidth - fill2) + "]";
|
|
60490
|
+
}
|
|
60454
60491
|
var init_usage_windows = __esm(() => {
|
|
60455
60492
|
init_jsonl();
|
|
60456
60493
|
init_usage_types();
|
|
@@ -61708,7 +61745,11 @@ class ResetTimerWidget {
|
|
|
61708
61745
|
const data = fetchApiData();
|
|
61709
61746
|
if (data.error)
|
|
61710
61747
|
return getErrorMessage(data.error);
|
|
61711
|
-
|
|
61748
|
+
const modelId = context.data?.model?.id ?? "";
|
|
61749
|
+
const is1mModel = modelId.includes("[1m]");
|
|
61750
|
+
const isOpus = modelId.includes("opus");
|
|
61751
|
+
const isChargedModel = is1mModel && !isOpus;
|
|
61752
|
+
if (data.extraUsageEnabled && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined && (data.weeklyUsage !== undefined && data.weeklyUsage >= 100 || isChargedModel)) {
|
|
61712
61753
|
const used = formatCents(data.extraUsageUsed);
|
|
61713
61754
|
const displayLimit = settings.extraUsageBalance ?? data.extraUsageLimit;
|
|
61714
61755
|
const limit = formatCents(displayLimit);
|
|
@@ -62453,6 +62494,169 @@ var init_VimMode = __esm(() => {
|
|
|
62453
62494
|
FORMATS = ["icon-dash-letter", "icon-letter", "icon", "letter", "word"];
|
|
62454
62495
|
});
|
|
62455
62496
|
|
|
62497
|
+
// src/widgets/WeeklyPace.ts
|
|
62498
|
+
function getPaceDisplayMode(item) {
|
|
62499
|
+
return item.metadata?.display === "pendulum" ? "pendulum" : "text";
|
|
62500
|
+
}
|
|
62501
|
+
function computePace(actualPercent, expectedPercent) {
|
|
62502
|
+
const delta = actualPercent - expectedPercent;
|
|
62503
|
+
const dayOfWeek = Math.max(1, Math.min(7, Math.ceil(expectedPercent * 7 / 100)));
|
|
62504
|
+
let status;
|
|
62505
|
+
if (delta > 15) {
|
|
62506
|
+
status = `Overcooking +${Math.round(delta)}%`;
|
|
62507
|
+
} else if (delta > 5) {
|
|
62508
|
+
status = `Warm +${Math.round(delta)}%`;
|
|
62509
|
+
} else if (delta < -15) {
|
|
62510
|
+
status = `Underusing ${Math.round(delta)}%`;
|
|
62511
|
+
} else if (delta < -5) {
|
|
62512
|
+
status = `Cool ${Math.round(delta)}%`;
|
|
62513
|
+
} else {
|
|
62514
|
+
status = "On Pace";
|
|
62515
|
+
}
|
|
62516
|
+
return { delta, dayOfWeek, status };
|
|
62517
|
+
}
|
|
62518
|
+
|
|
62519
|
+
class WeeklyPaceWidget {
|
|
62520
|
+
getDefaultColor() {
|
|
62521
|
+
return "brightYellow";
|
|
62522
|
+
}
|
|
62523
|
+
getDescription() {
|
|
62524
|
+
return "Shows if weekly usage pace is on track, overcooking, or underutilized";
|
|
62525
|
+
}
|
|
62526
|
+
getDisplayName() {
|
|
62527
|
+
return "Weekly Pace";
|
|
62528
|
+
}
|
|
62529
|
+
getCategory() {
|
|
62530
|
+
return "Usage";
|
|
62531
|
+
}
|
|
62532
|
+
getEditorDisplay(item) {
|
|
62533
|
+
const mode = getPaceDisplayMode(item);
|
|
62534
|
+
const modifiers = [];
|
|
62535
|
+
if (mode === "pendulum") {
|
|
62536
|
+
modifiers.push("pendulum bar");
|
|
62537
|
+
}
|
|
62538
|
+
return {
|
|
62539
|
+
displayText: this.getDisplayName(),
|
|
62540
|
+
modifierText: makeModifierText(modifiers)
|
|
62541
|
+
};
|
|
62542
|
+
}
|
|
62543
|
+
handleEditorAction(action, item) {
|
|
62544
|
+
if (action !== "toggle-pendulum") {
|
|
62545
|
+
return null;
|
|
62546
|
+
}
|
|
62547
|
+
const currentMode = getPaceDisplayMode(item);
|
|
62548
|
+
const nextMode = currentMode === "text" ? "pendulum" : "text";
|
|
62549
|
+
return {
|
|
62550
|
+
...item,
|
|
62551
|
+
metadata: {
|
|
62552
|
+
...item.metadata ?? {},
|
|
62553
|
+
display: nextMode
|
|
62554
|
+
}
|
|
62555
|
+
};
|
|
62556
|
+
}
|
|
62557
|
+
render(item, context, settings) {
|
|
62558
|
+
const displayMode = getPaceDisplayMode(item);
|
|
62559
|
+
if (context.isPreview) {
|
|
62560
|
+
if (displayMode === "pendulum") {
|
|
62561
|
+
const barDisplay = `${makePendulumBar(10)} D4/7 +10%`;
|
|
62562
|
+
return formatRawOrLabeledValue(item, "Pace: ", barDisplay);
|
|
62563
|
+
}
|
|
62564
|
+
return formatRawOrLabeledValue(item, "", "D4/7: On Pace");
|
|
62565
|
+
}
|
|
62566
|
+
const data = context.usageData;
|
|
62567
|
+
if (!data)
|
|
62568
|
+
return null;
|
|
62569
|
+
if (data.error)
|
|
62570
|
+
return getUsageErrorMessage(data.error);
|
|
62571
|
+
if (data.weeklyUsage === undefined)
|
|
62572
|
+
return null;
|
|
62573
|
+
const window2 = resolveWeeklyUsageWindow(data);
|
|
62574
|
+
if (!window2)
|
|
62575
|
+
return null;
|
|
62576
|
+
const actualPercent = Math.max(0, Math.min(100, data.weeklyUsage));
|
|
62577
|
+
const { delta, dayOfWeek, status } = computePace(actualPercent, window2.elapsedPercent);
|
|
62578
|
+
const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < MOBILE_THRESHOLD3;
|
|
62579
|
+
if (displayMode === "pendulum" && !mobile) {
|
|
62580
|
+
const sign = delta >= 0 ? "+" : "";
|
|
62581
|
+
const barDisplay = `${makePendulumBar(delta)} D${dayOfWeek}/7 ${sign}${Math.round(delta)}%`;
|
|
62582
|
+
return formatRawOrLabeledValue(item, "Pace: ", barDisplay);
|
|
62583
|
+
}
|
|
62584
|
+
return formatRawOrLabeledValue(item, "", `D${dayOfWeek}/7: ${status}`);
|
|
62585
|
+
}
|
|
62586
|
+
getCustomKeybinds() {
|
|
62587
|
+
return [
|
|
62588
|
+
{ key: "p", label: "(p)endulum toggle", action: "toggle-pendulum" }
|
|
62589
|
+
];
|
|
62590
|
+
}
|
|
62591
|
+
supportsRawValue() {
|
|
62592
|
+
return true;
|
|
62593
|
+
}
|
|
62594
|
+
supportsColors(item) {
|
|
62595
|
+
return true;
|
|
62596
|
+
}
|
|
62597
|
+
}
|
|
62598
|
+
var MOBILE_THRESHOLD3 = 80;
|
|
62599
|
+
var init_WeeklyPace = __esm(() => {
|
|
62600
|
+
init_usage();
|
|
62601
|
+
});
|
|
62602
|
+
|
|
62603
|
+
// src/widgets/OffPeak.ts
|
|
62604
|
+
function isOffPeak(now2) {
|
|
62605
|
+
const ms = now2.getTime();
|
|
62606
|
+
if (ms < PROMO_START_MS || ms >= PROMO_END_MS) {
|
|
62607
|
+
return null;
|
|
62608
|
+
}
|
|
62609
|
+
const dayOfWeek = now2.getUTCDay();
|
|
62610
|
+
const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
|
|
62611
|
+
if (isWeekend)
|
|
62612
|
+
return true;
|
|
62613
|
+
const utcHour = now2.getUTCHours();
|
|
62614
|
+
const isPeak = utcHour >= PEAK_START_UTC_HOUR && utcHour < PEAK_END_UTC_HOUR;
|
|
62615
|
+
return !isPeak;
|
|
62616
|
+
}
|
|
62617
|
+
|
|
62618
|
+
class OffPeakWidget {
|
|
62619
|
+
getDefaultColor() {
|
|
62620
|
+
return "green";
|
|
62621
|
+
}
|
|
62622
|
+
getDescription() {
|
|
62623
|
+
return "Shows peak / off-peak 2x status during the March 2026 Anthropic usage promotion";
|
|
62624
|
+
}
|
|
62625
|
+
getDisplayName() {
|
|
62626
|
+
return "Off Peak";
|
|
62627
|
+
}
|
|
62628
|
+
getCategory() {
|
|
62629
|
+
return "Usage";
|
|
62630
|
+
}
|
|
62631
|
+
getEditorDisplay(_item) {
|
|
62632
|
+
return { displayText: this.getDisplayName() };
|
|
62633
|
+
}
|
|
62634
|
+
render(item, context, _settings) {
|
|
62635
|
+
if (context.isPreview) {
|
|
62636
|
+
return item.rawValue ? "Off-peak 2x" : "Off peak: Off-peak 2x";
|
|
62637
|
+
}
|
|
62638
|
+
const result2 = isOffPeak(new Date);
|
|
62639
|
+
if (result2 === null)
|
|
62640
|
+
return null;
|
|
62641
|
+
const value = result2 ? "Off-peak 2x" : "Peak";
|
|
62642
|
+
const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < 80;
|
|
62643
|
+
if (item.rawValue || mobile)
|
|
62644
|
+
return value;
|
|
62645
|
+
return `Off peak: ${value}`;
|
|
62646
|
+
}
|
|
62647
|
+
supportsRawValue() {
|
|
62648
|
+
return true;
|
|
62649
|
+
}
|
|
62650
|
+
supportsColors(_item) {
|
|
62651
|
+
return true;
|
|
62652
|
+
}
|
|
62653
|
+
}
|
|
62654
|
+
var PROMO_START_MS, PROMO_END_MS, PEAK_START_UTC_HOUR = 12, PEAK_END_UTC_HOUR = 18;
|
|
62655
|
+
var init_OffPeak = __esm(() => {
|
|
62656
|
+
PROMO_START_MS = Date.UTC(2026, 2, 13, 7, 0, 0);
|
|
62657
|
+
PROMO_END_MS = Date.UTC(2026, 2, 29, 6, 59, 0);
|
|
62658
|
+
});
|
|
62659
|
+
|
|
62456
62660
|
// src/widgets/index.ts
|
|
62457
62661
|
var init_widgets = __esm(async () => {
|
|
62458
62662
|
init_GitBranch();
|
|
@@ -62471,6 +62675,8 @@ var init_widgets = __esm(async () => {
|
|
|
62471
62675
|
init_ThinkingEffort();
|
|
62472
62676
|
init_Battery();
|
|
62473
62677
|
init_VimMode();
|
|
62678
|
+
init_WeeklyPace();
|
|
62679
|
+
init_OffPeak();
|
|
62474
62680
|
await __promiseAll([
|
|
62475
62681
|
init_TokensInput(),
|
|
62476
62682
|
init_TokensOutput(),
|
|
@@ -62530,7 +62736,9 @@ var init_widget_manifest = __esm(async () => {
|
|
|
62530
62736
|
{ type: "skills", create: () => new SkillsWidget },
|
|
62531
62737
|
{ type: "thinking-effort", create: () => new ThinkingEffortWidget },
|
|
62532
62738
|
{ type: "battery", create: () => new BatteryWidget },
|
|
62533
|
-
{ type: "vim-mode", create: () => new VimModeWidget }
|
|
62739
|
+
{ type: "vim-mode", create: () => new VimModeWidget },
|
|
62740
|
+
{ type: "weekly-pace", create: () => new WeeklyPaceWidget },
|
|
62741
|
+
{ type: "off-peak", create: () => new OffPeakWidget }
|
|
62534
62742
|
];
|
|
62535
62743
|
LAYOUT_WIDGET_MANIFEST = [
|
|
62536
62744
|
{
|
|
@@ -68934,7 +69142,8 @@ var USAGE_WIDGET_TYPES = new Set([
|
|
|
68934
69142
|
"weekly-usage",
|
|
68935
69143
|
"block-timer",
|
|
68936
69144
|
"reset-timer",
|
|
68937
|
-
"weekly-reset-timer"
|
|
69145
|
+
"weekly-reset-timer",
|
|
69146
|
+
"weekly-pace"
|
|
68938
69147
|
]);
|
|
68939
69148
|
function hasUsageDependentWidgets(lines) {
|
|
68940
69149
|
return lines.some((line) => line.some((item) => USAGE_WIDGET_TYPES.has(item.type)));
|