ccstatusline-usage 2.1.6 → 2.1.7

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
@@ -46,12 +46,19 @@
46
46
 
47
47
  ## 🆕 Recent Updates
48
48
 
49
+ ### [v2.1.7](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.7) - Battery widget, extra usage fix & upstream sync
50
+
51
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Battery widget** — shows `B: xx%` on macOS and Linux when on battery power (hidden when charging)
52
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Extra usage conditional display** — Extra spending widget now only appears when the weekly budget is fully used (100%), instead of always when extra usage is enabled
53
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Upstream sync** — Memory Usage widget (v2.0.29)
54
+
49
55
  ### [v2.1.6](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.6) - Upstream sync
50
56
 
51
57
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Upstream sync** — merged 9 upstream commits: Session Name widget, Git Root Dir widget, CWD home abbreviation, block timer caching, git widget refactor, Windows UTF-8 fix, widget picker UX, TUI editor input fix
52
58
 
53
- ### v2.0.26 - v2.0.28 - Performance, git internals, and workflow improvements
59
+ ### v2.0.26 - v2.0.29 - Performance, git internals, and workflow improvements
54
60
 
61
+ - **🧠 Memory Usage widget (v2.0.29)** - Added a new widget that shows current system memory usage (`Mem: used/total`).
55
62
  - **⚡ Block timer cache (v2.0.28)** - Cache block timer metrics to reduce JSONL parsing on every render, with per-config hashed cache files and automatic 5-hour block invalidation.
56
63
  - **🧱 Git widget command refactor (v2.0.28)** - Refactored git widgets to use shared git command helpers and expanded coverage for failure and edge-case tests.
57
64
  - **🪟 Windows UTF-8 piped output fix (v2.0.28)** - Sets the Windows UTF-8 code page for piped status line rendering.
@@ -385,6 +392,8 @@ Once configured, ccstatusline automatically formats your Claude Code status line
385
392
  - **Context Percentage** - Shows percentage of context limit used (dynamic: 1M for Sonnet 4.5 with `[1m]` suffix, 200k otherwise)
386
393
  - **Context Percentage (usable)** - Shows percentage of usable context (dynamic: 800k for Sonnet 4.5 with `[1m]` suffix, 160k otherwise, accounting for auto-compact at 80%)
387
394
  - **Terminal Width** - Shows detected terminal width (for debugging)
395
+ - **Memory Usage** - Shows system memory usage (used/total, e.g., "Mem: 12.4G/16.0G")
396
+ - **Battery** - Shows battery percentage on macOS and Linux (only visible when on battery power, hidden when charging)
388
397
  - **Custom Text** - Add your own custom text to the status line
389
398
  - **Custom Command** - Execute shell commands and display their output (refreshes whenever the statusline is updated by Claude Code)
390
399
  - **Separator** - Visual divider between widgets (customizable: |, -, comma, space)
@@ -618,6 +627,14 @@ Contributions are welcome! Please feel free to submit a Pull Request.
618
627
 
619
628
  ---
620
629
 
630
+ ## Support
631
+
632
+ If ccstatusline is useful to you, consider buying me a coffee:
633
+
634
+ <a href="https://www.buymeacoffee.com/sirmalloc" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
635
+
636
+ ---
637
+
621
638
  ## 📄 License
622
639
 
623
640
  [MIT](LICENSE) © Matthew Breedlove
@@ -50987,6 +50987,8 @@ var SettingsSchema = exports_external.object({
50987
50987
  { id: "weekly-usage", type: "weekly-usage", color: "brightBlue" },
50988
50988
  { id: "sep2", type: "separator" },
50989
50989
  { id: "reset-timer", type: "reset-timer", color: "brightBlue" },
50990
+ { id: "sep-battery", type: "separator" },
50991
+ { id: "battery", type: "battery", color: "yellow" },
50990
50992
  { id: "sep3", type: "separator" },
50991
50993
  { id: "model", type: "model", color: "ansi256:124" },
50992
50994
  { id: "sep4", type: "separator" },
@@ -51450,7 +51452,7 @@ import { execSync as execSync3 } from "child_process";
51450
51452
  import * as fs5 from "fs";
51451
51453
  import * as path4 from "path";
51452
51454
  var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils";
51453
- var PACKAGE_VERSION = "2.1.6";
51455
+ var PACKAGE_VERSION = "2.1.7";
51454
51456
  function getPackageVersion() {
51455
51457
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51456
51458
  return PACKAGE_VERSION;
@@ -55003,7 +55005,7 @@ class ResetTimerWidget {
55003
55005
  const data = fetchApiData();
55004
55006
  if (data.error)
55005
55007
  return getErrorMessage(data.error);
55006
- if (data.extraUsageEnabled && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined) {
55008
+ if (data.extraUsageEnabled && data.weeklyUsage !== undefined && data.weeklyUsage >= 100 && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined) {
55007
55009
  const used = formatCents(data.extraUsageUsed);
55008
55010
  const limit = formatCents(data.extraUsageLimit);
55009
55011
  return `Extra: ${used}/${limit}`;
@@ -55143,6 +55145,167 @@ class SessionNameWidget {
55143
55145
  return true;
55144
55146
  }
55145
55147
  }
55148
+ // src/widgets/FreeMemory.ts
55149
+ import { execSync as execSync7 } from "child_process";
55150
+ import os6 from "os";
55151
+ function formatBytes(bytes) {
55152
+ const GB = 1024 ** 3;
55153
+ const MB = 1024 ** 2;
55154
+ const KB = 1024;
55155
+ if (bytes >= GB)
55156
+ return `${(bytes / GB).toFixed(1)}G`;
55157
+ if (bytes >= MB)
55158
+ return `${(bytes / MB).toFixed(0)}M`;
55159
+ if (bytes >= KB)
55160
+ return `${(bytes / KB).toFixed(0)}K`;
55161
+ return `${bytes}B`;
55162
+ }
55163
+ function getUsedMemoryMacOS() {
55164
+ try {
55165
+ const output = execSync7("vm_stat", { encoding: "utf8" });
55166
+ const lines = output.split(`
55167
+ `);
55168
+ const firstLine = lines[0];
55169
+ if (!firstLine)
55170
+ return null;
55171
+ const pageSizeMatch = /page size of (\d+) bytes/.exec(firstLine);
55172
+ const pageSizeString = pageSizeMatch?.[1];
55173
+ if (!pageSizeString)
55174
+ return null;
55175
+ const pageSize = parseInt(pageSizeString, 10);
55176
+ let activePages = 0;
55177
+ let wiredPages = 0;
55178
+ for (const line of lines) {
55179
+ const activeMatch = /Pages active:\s+(\d+)/.exec(line);
55180
+ const activeValue = activeMatch?.[1];
55181
+ if (activeValue)
55182
+ activePages = parseInt(activeValue, 10);
55183
+ const wiredMatch = /Pages wired down:\s+(\d+)/.exec(line);
55184
+ const wiredValue = wiredMatch?.[1];
55185
+ if (wiredValue)
55186
+ wiredPages = parseInt(wiredValue, 10);
55187
+ }
55188
+ return (activePages + wiredPages) * pageSize;
55189
+ } catch {
55190
+ return null;
55191
+ }
55192
+ }
55193
+
55194
+ class FreeMemoryWidget {
55195
+ getDefaultColor() {
55196
+ return "cyan";
55197
+ }
55198
+ getDescription() {
55199
+ return "Shows system memory usage (used/total)";
55200
+ }
55201
+ getDisplayName() {
55202
+ return "Memory Usage";
55203
+ }
55204
+ getCategory() {
55205
+ return "Environment";
55206
+ }
55207
+ getEditorDisplay(item) {
55208
+ return { displayText: this.getDisplayName() };
55209
+ }
55210
+ render(item, context, settings) {
55211
+ if (context.isPreview) {
55212
+ return item.rawValue ? "12.4G/16.0G" : "Mem: 12.4G/16.0G";
55213
+ }
55214
+ const total = os6.totalmem();
55215
+ let used;
55216
+ if (os6.platform() === "darwin") {
55217
+ used = getUsedMemoryMacOS() ?? total - os6.freemem();
55218
+ } else {
55219
+ used = total - os6.freemem();
55220
+ }
55221
+ const value = `${formatBytes(used)}/${formatBytes(total)}`;
55222
+ return item.rawValue ? value : `Mem: ${value}`;
55223
+ }
55224
+ supportsRawValue() {
55225
+ return true;
55226
+ }
55227
+ supportsColors(item) {
55228
+ return true;
55229
+ }
55230
+ }
55231
+ // src/widgets/Battery.ts
55232
+ import { execSync as execSync8 } from "child_process";
55233
+ import { readFileSync as readFileSync6 } from "fs";
55234
+ function getMacBatteryInfo() {
55235
+ try {
55236
+ const output = execSync8("pmset -g batt", { encoding: "utf-8", timeout: 2000 });
55237
+ const match = /(\d+)%;\s*(charging|discharging|charged|finishing charge|AC attached)/i.exec(output);
55238
+ const percentStr = match?.[1];
55239
+ const stateStr = match?.[2];
55240
+ if (!percentStr || !stateStr) {
55241
+ return null;
55242
+ }
55243
+ const percent = parseInt(percentStr, 10);
55244
+ const state = stateStr.toLowerCase();
55245
+ const charging = state !== "discharging";
55246
+ return { percent, charging };
55247
+ } catch {
55248
+ return null;
55249
+ }
55250
+ }
55251
+ function getLinuxBatteryInfo() {
55252
+ try {
55253
+ const capacity = readFileSync6("/sys/class/power_supply/BAT0/capacity", "utf-8").trim();
55254
+ const status = readFileSync6("/sys/class/power_supply/BAT0/status", "utf-8").trim().toLowerCase();
55255
+ const percent = parseInt(capacity, 10);
55256
+ if (isNaN(percent)) {
55257
+ return null;
55258
+ }
55259
+ const charging = status !== "discharging";
55260
+ return { percent, charging };
55261
+ } catch {
55262
+ return null;
55263
+ }
55264
+ }
55265
+ function getBatteryInfo() {
55266
+ if (process.platform === "darwin") {
55267
+ return getMacBatteryInfo();
55268
+ }
55269
+ if (process.platform === "linux") {
55270
+ return getLinuxBatteryInfo();
55271
+ }
55272
+ return null;
55273
+ }
55274
+
55275
+ class BatteryWidget {
55276
+ getDefaultColor() {
55277
+ return "yellow";
55278
+ }
55279
+ getDescription() {
55280
+ return "Shows battery percentage (only when on battery, hidden when charging)";
55281
+ }
55282
+ getDisplayName() {
55283
+ return "Battery";
55284
+ }
55285
+ getCategory() {
55286
+ return "Environment";
55287
+ }
55288
+ getEditorDisplay(item) {
55289
+ return { displayText: this.getDisplayName() };
55290
+ }
55291
+ render(item, context, settings) {
55292
+ if (context.isPreview) {
55293
+ return item.rawValue ? "72%" : "B: 72%";
55294
+ }
55295
+ const info = getBatteryInfo();
55296
+ if (!info || info.charging) {
55297
+ return null;
55298
+ }
55299
+ const label = item.rawValue ? "" : "B: ";
55300
+ return `${label}${info.percent}%`;
55301
+ }
55302
+ supportsRawValue() {
55303
+ return true;
55304
+ }
55305
+ supportsColors(item) {
55306
+ return true;
55307
+ }
55308
+ }
55146
55309
  // src/utils/widgets.ts
55147
55310
  var widgetRegistry = new Map([
55148
55311
  ["model", new ModelWidget],
@@ -55171,7 +55334,9 @@ var widgetRegistry = new Map([
55171
55334
  ["weekly-usage", new WeeklyUsageWidget],
55172
55335
  ["reset-timer", new ResetTimerWidget],
55173
55336
  ["context-bar", new ContextBarWidget],
55174
- ["session-name", new SessionNameWidget]
55337
+ ["session-name", new SessionNameWidget],
55338
+ ["free-memory", new FreeMemoryWidget],
55339
+ ["battery", new BatteryWidget]
55175
55340
  ]);
55176
55341
  function getWidget(type) {
55177
55342
  return widgetRegistry.get(type) ?? null;
@@ -57531,7 +57696,7 @@ var MainMenu = ({ onSelect, isClaudeInstalled, hasChanges, initialSelection = 0,
57531
57696
  };
57532
57697
  // src/tui/components/PowerlineSetup.tsx
57533
57698
  var import_react41 = __toESM(require_react(), 1);
57534
- import * as os6 from "os";
57699
+ import * as os7 from "os";
57535
57700
 
57536
57701
  // src/tui/components/PowerlineSeparatorEditor.tsx
57537
57702
  var import_react39 = __toESM(require_react(), 1);
@@ -58248,7 +58413,7 @@ var PowerlineSetup = ({
58248
58413
  }, undefined, false, undefined, this)
58249
58414
  ]
58250
58415
  }, undefined, true, undefined, this),
58251
- os6.platform() === "darwin" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58416
+ os7.platform() === "darwin" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58252
58417
  children: [
58253
58418
  /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
58254
58419
  dimColor: true,
@@ -58264,7 +58429,7 @@ var PowerlineSetup = ({
58264
58429
  }, undefined, false, undefined, this)
58265
58430
  ]
58266
58431
  }, undefined, true, undefined, this),
58267
- os6.platform() === "linux" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58432
+ os7.platform() === "linux" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58268
58433
  children: [
58269
58434
  /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
58270
58435
  dimColor: true,
@@ -58280,7 +58445,7 @@ var PowerlineSetup = ({
58280
58445
  }, undefined, false, undefined, this)
58281
58446
  ]
58282
58447
  }, undefined, true, undefined, this),
58283
- os6.platform() === "win32" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58448
+ os7.platform() === "win32" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58284
58449
  children: [
58285
58450
  /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
58286
58451
  dimColor: true,
@@ -59537,7 +59702,7 @@ function renderCompactOutput(preRenderedLines, settings, maxWidth) {
59537
59702
  // src/utils/jsonl.ts
59538
59703
  import * as fs9 from "fs";
59539
59704
  import { createHash } from "node:crypto";
59540
- import os7 from "node:os";
59705
+ import os8 from "node:os";
59541
59706
  import path8 from "node:path";
59542
59707
 
59543
59708
  // node_modules/tinyglobby/dist/index.mjs
@@ -60327,7 +60492,7 @@ function globSync(patternsOrOptions, options) {
60327
60492
  // src/utils/jsonl.ts
60328
60493
  import { promisify } from "util";
60329
60494
  var readFile4 = promisify(fs9.readFile);
60330
- var readFileSync7 = fs9.readFileSync;
60495
+ var readFileSync8 = fs9.readFileSync;
60331
60496
  var statSync5 = fs9.statSync;
60332
60497
  var writeFileSync3 = fs9.writeFileSync;
60333
60498
  var mkdirSync4 = fs9.mkdirSync;
@@ -60338,7 +60503,7 @@ function normalizeConfigDir(configDir) {
60338
60503
  function getBlockCachePath(configDir = getClaudeConfigDir()) {
60339
60504
  const normalizedConfigDir = normalizeConfigDir(configDir);
60340
60505
  const configHash = createHash("sha256").update(normalizedConfigDir).digest("hex").slice(0, 16);
60341
- return path8.join(os7.homedir(), ".cache", "ccstatusline", `block-cache-${configHash}.json`);
60506
+ return path8.join(os8.homedir(), ".cache", "ccstatusline", `block-cache-${configHash}.json`);
60342
60507
  }
60343
60508
  function readBlockCache(expectedConfigDir) {
60344
60509
  try {
@@ -60347,7 +60512,7 @@ function readBlockCache(expectedConfigDir) {
60347
60512
  if (!existsSync8(cachePath)) {
60348
60513
  return null;
60349
60514
  }
60350
- const content = readFileSync7(cachePath, "utf-8");
60515
+ const content = readFileSync8(cachePath, "utf-8");
60351
60516
  const cache3 = JSON.parse(content);
60352
60517
  if (typeof cache3.startTime !== "string") {
60353
60518
  return null;
@@ -60605,7 +60770,7 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
60605
60770
  function getAllTimestampsFromFile(filePath) {
60606
60771
  const timestamps = [];
60607
60772
  try {
60608
- const content = readFileSync7(filePath, "utf-8");
60773
+ const content = readFileSync8(filePath, "utf-8");
60609
60774
  const lines = content.trim().split(`
60610
60775
  `).filter((line) => line.length > 0);
60611
60776
  for (const line of lines) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline-usage",
3
- "version": "2.1.6",
3
+ "version": "2.1.7",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",