ccstatusline-usage 2.3.0 → 2.3.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 CHANGED
@@ -31,6 +31,7 @@
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
36
37
  - **Two-line Layout** - Session info on line 1, context on line 2
@@ -39,7 +40,7 @@ This fork adds API-based usage widgets beyond the upstream:
39
40
 
40
41
  ```
41
42
  Session: [████░░░░░░░░░░░] 27.0% | Weekly: [███████████████] 100.0% | Extra: €2.50/€50.00 | Model: Opus 4.6 | Session ID: 0109b99d...
42
- Context: [███████░░░░░░░░] 103k/200k (51%)
43
+ Context: [███████░░░░░░░░] 103k/200k (51%) | Pace: [░░░░░░░|██░░░░░] D5/7 +12%
43
44
  ```
44
45
 
45
46
  ![Demo](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/demo.gif)
@@ -64,6 +65,10 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
64
65
 
65
66
  ## 🆕 Recent Updates
66
67
 
68
+ ### [v2.3.1](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.1) - Weekly Pace widget
69
+
70
+ - [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)
71
+
67
72
  ### [v2.3.0](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.0) - Upstream sync + Vim Mode widget
68
73
 
69
74
  - [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,6 +543,7 @@ bun run example
538
543
  - **Battery** *(ccstatusline-usage)* - Shows battery percentage on macOS and Linux (only visible when on battery power, hidden when charging)
539
544
  - **Session Usage** - Shows daily/session API usage percentage
540
545
  - **Weekly Usage** - Shows weekly API usage percentage
546
+ - **Weekly Pace** - Pendulum bar or text label showing if usage pace is on track, overcooking, or underutilized (toggle with `p` key in TUI)
541
547
  - **Block Reset Timer** - Shows time remaining until current 5-hour block reset window
542
548
  - **Weekly Reset Timer** - Shows time remaining until weekly usage reset
543
549
  - **Context Bar** - Shows context usage as a progress bar with short/full display modes
@@ -631,6 +637,7 @@ Widget-specific shortcuts:
631
637
  - **Block Timer**: `p` cycle display mode (time/full bar/short bar)
632
638
  - **Block Reset Timer**: `p` cycle display mode (time/full bar/short bar)
633
639
  - **Weekly Reset Timer**: `p` cycle display mode (time/full bar/short bar)
640
+ - **Weekly Pace**: `p` toggle pendulum bar / text label
634
641
  - **Current Working Dir**: `h` home abbreviation, `s` segment editor, `f` fish-style path
635
642
  - **Custom Command**: `e` command, `w` max width, `t` timeout, `p` preserve ANSI colors
636
643
  - **Link**: `u` URL, `e` link text
@@ -54069,7 +54069,7 @@ function getTerminalWidth() {
54069
54069
  function canDetectTerminalWidth() {
54070
54070
  return probeTerminalWidth() !== null;
54071
54071
  }
54072
- var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.0";
54072
+ var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.1";
54073
54073
  var init_terminal = () => {};
54074
54074
 
54075
54075
  // src/utils/renderer.ts
@@ -60440,6 +60440,19 @@ function resolveUsageWindowWithFallback(usageData, blockMetrics, nowMs = Date.no
60440
60440
  }
60441
60441
  return getUsageWindowFromBlockMetrics(fallbackMetrics, nowMs);
60442
60442
  }
60443
+ function getWeeklyUsageWindowFromResetAt(weeklyResetAt, nowMs = Date.now()) {
60444
+ if (!weeklyResetAt) {
60445
+ return null;
60446
+ }
60447
+ const resetAtMs = Date.parse(weeklyResetAt);
60448
+ if (Number.isNaN(resetAtMs)) {
60449
+ return null;
60450
+ }
60451
+ return buildUsageWindow(resetAtMs, nowMs, SEVEN_DAY_WINDOW_MS);
60452
+ }
60453
+ function resolveWeeklyUsageWindow(usageData, nowMs = Date.now()) {
60454
+ return getWeeklyUsageWindowFromResetAt(usageData.weeklyResetAt, nowMs);
60455
+ }
60443
60456
  function formatUsageDuration(durationMs, compact2 = false, useDays = true) {
60444
60457
  const clampedMs = Math.max(0, durationMs);
60445
60458
  const totalHours = Math.floor(clampedMs / (1000 * 60 * 60));
@@ -60451,6 +60464,28 @@ function formatUsageDuration(durationMs, compact2 = false, useDays = true) {
60451
60464
  const parts = [d > 0 && `${d}d`, h > 0 && `${h}${hLabel}`, m > 0 && `${m}m`].filter(Boolean);
60452
60465
  return parts.length > 0 ? parts.join(sep2) : "0m";
60453
60466
  }
60467
+ function getUsageErrorMessage(error48) {
60468
+ switch (error48) {
60469
+ case "no-credentials":
60470
+ return "[No credentials]";
60471
+ case "timeout":
60472
+ return "[Timeout]";
60473
+ case "rate-limited":
60474
+ return "[Rate limited]";
60475
+ case "api-error":
60476
+ return "[API Error]";
60477
+ case "parse-error":
60478
+ return "[Parse Error]";
60479
+ }
60480
+ }
60481
+ function makePendulumBar(delta, halfWidth = 7) {
60482
+ const clamped = Math.max(-100, Math.min(100, delta));
60483
+ const fill2 = Math.min(halfWidth, Math.round(Math.abs(clamped) / 100 * halfWidth));
60484
+ if (clamped < 0) {
60485
+ return "[" + "░".repeat(halfWidth - fill2) + "█".repeat(fill2) + "|" + "░".repeat(halfWidth) + "]";
60486
+ }
60487
+ return "[" + "░".repeat(halfWidth) + "|" + "█".repeat(fill2) + "░".repeat(halfWidth - fill2) + "]";
60488
+ }
60454
60489
  var init_usage_windows = __esm(() => {
60455
60490
  init_jsonl();
60456
60491
  init_usage_types();
@@ -62453,6 +62488,110 @@ var init_VimMode = __esm(() => {
62453
62488
  FORMATS = ["icon-dash-letter", "icon-letter", "icon", "letter", "word"];
62454
62489
  });
62455
62490
 
62491
+ // src/widgets/WeeklyPace.ts
62492
+ function getPaceDisplayMode(item) {
62493
+ return item.metadata?.display === "pendulum" ? "pendulum" : "text";
62494
+ }
62495
+ function computePace(actualPercent, expectedPercent) {
62496
+ const delta = actualPercent - expectedPercent;
62497
+ const dayOfWeek = Math.max(1, Math.min(7, Math.ceil(expectedPercent * 7 / 100)));
62498
+ let status;
62499
+ if (delta > 15) {
62500
+ status = `Overcooking +${Math.round(delta)}%`;
62501
+ } else if (delta > 5) {
62502
+ status = `Warm +${Math.round(delta)}%`;
62503
+ } else if (delta < -15) {
62504
+ status = `Underusing ${Math.round(delta)}%`;
62505
+ } else if (delta < -5) {
62506
+ status = `Cool ${Math.round(delta)}%`;
62507
+ } else {
62508
+ status = "On Pace";
62509
+ }
62510
+ return { delta, dayOfWeek, status };
62511
+ }
62512
+
62513
+ class WeeklyPaceWidget {
62514
+ getDefaultColor() {
62515
+ return "brightYellow";
62516
+ }
62517
+ getDescription() {
62518
+ return "Shows if weekly usage pace is on track, overcooking, or underutilized";
62519
+ }
62520
+ getDisplayName() {
62521
+ return "Weekly Pace";
62522
+ }
62523
+ getCategory() {
62524
+ return "Usage";
62525
+ }
62526
+ getEditorDisplay(item) {
62527
+ const mode = getPaceDisplayMode(item);
62528
+ const modifiers = [];
62529
+ if (mode === "pendulum") {
62530
+ modifiers.push("pendulum bar");
62531
+ }
62532
+ return {
62533
+ displayText: this.getDisplayName(),
62534
+ modifierText: makeModifierText(modifiers)
62535
+ };
62536
+ }
62537
+ handleEditorAction(action, item) {
62538
+ if (action !== "toggle-pendulum") {
62539
+ return null;
62540
+ }
62541
+ const currentMode = getPaceDisplayMode(item);
62542
+ const nextMode = currentMode === "text" ? "pendulum" : "text";
62543
+ return {
62544
+ ...item,
62545
+ metadata: {
62546
+ ...item.metadata ?? {},
62547
+ display: nextMode
62548
+ }
62549
+ };
62550
+ }
62551
+ render(item, context, settings) {
62552
+ const displayMode = getPaceDisplayMode(item);
62553
+ if (context.isPreview) {
62554
+ if (displayMode === "pendulum") {
62555
+ const barDisplay = `${makePendulumBar(10)} D4/7 +10%`;
62556
+ return formatRawOrLabeledValue(item, "Pace: ", barDisplay);
62557
+ }
62558
+ return formatRawOrLabeledValue(item, "", "D4/7: On Pace");
62559
+ }
62560
+ const data = context.usageData;
62561
+ if (!data)
62562
+ return null;
62563
+ if (data.error)
62564
+ return getUsageErrorMessage(data.error);
62565
+ if (data.weeklyUsage === undefined)
62566
+ return null;
62567
+ const window2 = resolveWeeklyUsageWindow(data);
62568
+ if (!window2)
62569
+ return null;
62570
+ const actualPercent = Math.max(0, Math.min(100, data.weeklyUsage));
62571
+ const { delta, dayOfWeek, status } = computePace(actualPercent, window2.elapsedPercent);
62572
+ if (displayMode === "pendulum") {
62573
+ const sign = delta >= 0 ? "+" : "";
62574
+ const barDisplay = `${makePendulumBar(delta)} D${dayOfWeek}/7 ${sign}${Math.round(delta)}%`;
62575
+ return formatRawOrLabeledValue(item, "Pace: ", barDisplay);
62576
+ }
62577
+ return formatRawOrLabeledValue(item, "", `D${dayOfWeek}/7: ${status}`);
62578
+ }
62579
+ getCustomKeybinds() {
62580
+ return [
62581
+ { key: "p", label: "(p)endulum toggle", action: "toggle-pendulum" }
62582
+ ];
62583
+ }
62584
+ supportsRawValue() {
62585
+ return true;
62586
+ }
62587
+ supportsColors(item) {
62588
+ return true;
62589
+ }
62590
+ }
62591
+ var init_WeeklyPace = __esm(() => {
62592
+ init_usage();
62593
+ });
62594
+
62456
62595
  // src/widgets/index.ts
62457
62596
  var init_widgets = __esm(async () => {
62458
62597
  init_GitBranch();
@@ -62471,6 +62610,7 @@ var init_widgets = __esm(async () => {
62471
62610
  init_ThinkingEffort();
62472
62611
  init_Battery();
62473
62612
  init_VimMode();
62613
+ init_WeeklyPace();
62474
62614
  await __promiseAll([
62475
62615
  init_TokensInput(),
62476
62616
  init_TokensOutput(),
@@ -62530,7 +62670,8 @@ var init_widget_manifest = __esm(async () => {
62530
62670
  { type: "skills", create: () => new SkillsWidget },
62531
62671
  { type: "thinking-effort", create: () => new ThinkingEffortWidget },
62532
62672
  { type: "battery", create: () => new BatteryWidget },
62533
- { type: "vim-mode", create: () => new VimModeWidget }
62673
+ { type: "vim-mode", create: () => new VimModeWidget },
62674
+ { type: "weekly-pace", create: () => new WeeklyPaceWidget }
62534
62675
  ];
62535
62676
  LAYOUT_WIDGET_MANIFEST = [
62536
62677
  {
@@ -68934,7 +69075,8 @@ var USAGE_WIDGET_TYPES = new Set([
68934
69075
  "weekly-usage",
68935
69076
  "block-timer",
68936
69077
  "reset-timer",
68937
- "weekly-reset-timer"
69078
+ "weekly-reset-timer",
69079
+ "weekly-pace"
68938
69080
  ]);
68939
69081
  function hasUsageDependentWidgets(lines) {
68940
69082
  return lines.some((line) => line.some((item) => USAGE_WIDGET_TYPES.has(item.type)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline-usage",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",