ccstatusline 2.0.8 → 2.0.10

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
@@ -22,6 +22,8 @@
22
22
  [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/sirmalloc/ccstatusline/graphs/commit-activity)
23
23
 
24
24
  [![Mentioned in Awesome Claude Code](https://awesome.re/mentioned-badge.svg)](https://github.com/hesreallyhim/awesome-claude-code)
25
+ [![ClaudeLog - A comprehensive knowledge base for Claude](https://claudelog.com/img/claude_log_badge.svg)](https://claudelog.com/)
26
+
25
27
 
26
28
  ![Demo](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/demo.gif)
27
29
 
@@ -42,6 +44,11 @@
42
44
 
43
45
  ## 🆕 Recent Updates
44
46
 
47
+ ### v2.0.10 - Git Updates
48
+
49
+ - **🌳 Git Worktree widget** - Shows the active worktree name when working with git worktrees
50
+ - **👻 Hide 'no git' message toggle** - Git widgets now support hiding the 'no git' message when not in a repository (toggle with 'h' key while editing the widget)
51
+
45
52
  ### v2.0.8 - Powerline Auto-Alignment
46
53
 
47
54
  ![Powerline Auto-Alignment](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/autoAlign.png)
@@ -137,6 +144,7 @@ Once configured, ccstatusline automatically formats your Claude Code status line
137
144
  - **Model Name** - Shows the current Claude model (e.g., "Claude 3.5 Sonnet")
138
145
  - **Git Branch** - Displays current git branch name
139
146
  - **Git Changes** - Shows uncommitted insertions/deletions (e.g., "+42,-10")
147
+ - **Git Worktree** - Shows the name of the current git worktree
140
148
  - **Session Clock** - Shows elapsed time since session start (e.g., "2hr 15m")
141
149
  - **Session Cost** - Shows total session cost in USD (e.g., "$1.23")
142
150
  - **Block Timer** - Shows time elapsed in current 5-hour block or progress bar
@@ -285,7 +293,7 @@ When terminal width is detected, status lines automatically truncate with ellips
285
293
 
286
294
  ```bash
287
295
  # Clone the repository
288
- git clone https://github.com/yourusername/ccstatusline.git
296
+ git clone https://github.com/sirmalloc/ccstatusline.git
289
297
  cd ccstatusline
290
298
 
291
299
  # Install dependencies
@@ -36263,7 +36263,7 @@ function wrapAnsi(string, columns, options) {
36263
36263
  `);
36264
36264
  }
36265
36265
 
36266
- // node_modules/is-fullwidth-code-point/index.js
36266
+ // node_modules/cli-truncate/node_modules/slice-ansi/node_modules/is-fullwidth-code-point/index.js
36267
36267
  function isFullwidthCodePoint(codePoint) {
36268
36268
  if (!Number.isInteger(codePoint)) {
36269
36269
  return false;
@@ -37556,6 +37556,14 @@ function styledCharsToString(chars) {
37556
37556
  }
37557
37557
  return ret;
37558
37558
  }
37559
+ // node_modules/is-fullwidth-code-point/index.js
37560
+ function isFullwidthCodePoint3(codePoint) {
37561
+ if (!Number.isInteger(codePoint)) {
37562
+ return false;
37563
+ }
37564
+ return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || 11904 <= codePoint && codePoint <= 12871 && codePoint !== 12351 || 12880 <= codePoint && codePoint <= 19903 || 19968 <= codePoint && codePoint <= 42182 || 43360 <= codePoint && codePoint <= 43388 || 44032 <= codePoint && codePoint <= 55203 || 63744 <= codePoint && codePoint <= 64255 || 65040 <= codePoint && codePoint <= 65049 || 65072 <= codePoint && codePoint <= 65131 || 65281 <= codePoint && codePoint <= 65376 || 65504 <= codePoint && codePoint <= 65510 || 110592 <= codePoint && codePoint <= 110593 || 127488 <= codePoint && codePoint <= 127569 || 131072 <= codePoint && codePoint <= 262141);
37565
+ }
37566
+
37559
37567
  // node_modules/@alcalzone/ansi-tokenize/build/tokenize.js
37560
37568
  function findNumberIndex2(str) {
37561
37569
  for (let index = 0;index < str.length; index++) {
@@ -37607,7 +37615,7 @@ function tokenize2(str, endChar = Number.POSITIVE_INFINITY) {
37607
37615
  continue;
37608
37616
  }
37609
37617
  }
37610
- const fullWidth = isFullwidthCodePoint(codePoint);
37618
+ const fullWidth = isFullwidthCodePoint3(codePoint);
37611
37619
  const character = String.fromCodePoint(codePoint);
37612
37620
  ret.push({
37613
37621
  type: "char",
@@ -51037,7 +51045,7 @@ import { execSync as execSync3 } from "child_process";
51037
51045
  import * as fs5 from "fs";
51038
51046
  import * as path4 from "path";
51039
51047
  var __dirname = "/Users/sirmalloc/Projects/Personal/ccstatusline/src/utils";
51040
- var PACKAGE_VERSION = "2.0.8";
51048
+ var PACKAGE_VERSION = "2.0.10";
51041
51049
  function getPackageVersion() {
51042
51050
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51043
51051
  return PACKAGE_VERSION;
@@ -51938,16 +51946,38 @@ class GitBranchWidget {
51938
51946
  return "Git Branch";
51939
51947
  }
51940
51948
  getEditorDisplay(item) {
51941
- return { displayText: this.getDisplayName() };
51949
+ const hideNoGit = item.metadata?.hideNoGit === "true";
51950
+ const modifiers = [];
51951
+ if (hideNoGit) {
51952
+ modifiers.push("hide 'no git'");
51953
+ }
51954
+ return {
51955
+ displayText: this.getDisplayName(),
51956
+ modifierText: modifiers.length > 0 ? `(${modifiers.join(", ")})` : undefined
51957
+ };
51958
+ }
51959
+ handleEditorAction(action, item) {
51960
+ if (action === "toggle-nogit") {
51961
+ const currentState = item.metadata?.hideNoGit === "true";
51962
+ return {
51963
+ ...item,
51964
+ metadata: {
51965
+ ...item.metadata,
51966
+ hideNoGit: (!currentState).toString()
51967
+ }
51968
+ };
51969
+ }
51970
+ return null;
51942
51971
  }
51943
51972
  render(item, context, settings) {
51973
+ const hideNoGit = item.metadata?.hideNoGit === "true";
51944
51974
  if (context.isPreview) {
51945
51975
  return item.rawValue ? "main" : "⎇ main";
51946
51976
  }
51947
51977
  const branch = this.getGitBranch();
51948
51978
  if (branch)
51949
51979
  return item.rawValue ? branch : `⎇ ${branch}`;
51950
- return "⎇ no git";
51980
+ return hideNoGit ? null : "⎇ no git";
51951
51981
  }
51952
51982
  getGitBranch() {
51953
51983
  try {
@@ -51960,6 +51990,11 @@ class GitBranchWidget {
51960
51990
  return null;
51961
51991
  }
51962
51992
  }
51993
+ getCustomKeybinds() {
51994
+ return [
51995
+ { key: "h", label: "(h)ide 'no git' message", action: "toggle-nogit" }
51996
+ ];
51997
+ }
51963
51998
  supportsRawValue() {
51964
51999
  return true;
51965
52000
  }
@@ -51981,9 +52016,31 @@ class GitChangesWidget {
51981
52016
  return "Git Changes";
51982
52017
  }
51983
52018
  getEditorDisplay(item) {
51984
- return { displayText: this.getDisplayName() };
52019
+ const hideNoGit = item.metadata?.hideNoGit === "true";
52020
+ const modifiers = [];
52021
+ if (hideNoGit) {
52022
+ modifiers.push("hide 'no git'");
52023
+ }
52024
+ return {
52025
+ displayText: this.getDisplayName(),
52026
+ modifierText: modifiers.length > 0 ? `(${modifiers.join(", ")})` : undefined
52027
+ };
52028
+ }
52029
+ handleEditorAction(action, item) {
52030
+ if (action === "toggle-nogit") {
52031
+ const currentState = item.metadata?.hideNoGit === "true";
52032
+ return {
52033
+ ...item,
52034
+ metadata: {
52035
+ ...item.metadata,
52036
+ hideNoGit: (!currentState).toString()
52037
+ }
52038
+ };
52039
+ }
52040
+ return null;
51985
52041
  }
51986
52042
  render(item, context, settings) {
52043
+ const hideNoGit = item.metadata?.hideNoGit === "true";
51987
52044
  if (context.isPreview) {
51988
52045
  return "(+42,-10)";
51989
52046
  }
@@ -51991,7 +52048,7 @@ class GitChangesWidget {
51991
52048
  if (changes)
51992
52049
  return `(+${changes.insertions},-${changes.deletions})`;
51993
52050
  else
51994
- return "(no git)";
52051
+ return hideNoGit ? null : "(no git)";
51995
52052
  }
51996
52053
  getGitChanges() {
51997
52054
  try {
@@ -52022,6 +52079,11 @@ class GitChangesWidget {
52022
52079
  return null;
52023
52080
  }
52024
52081
  }
52082
+ getCustomKeybinds() {
52083
+ return [
52084
+ { key: "h", label: "(h)ide 'no git' message", action: "toggle-nogit" }
52085
+ ];
52086
+ }
52025
52087
  supportsRawValue() {
52026
52088
  return false;
52027
52089
  }
@@ -52029,6 +52091,78 @@ class GitChangesWidget {
52029
52091
  return true;
52030
52092
  }
52031
52093
  }
52094
+ // src/widgets/GitWorktree.ts
52095
+ import { execSync as execSync6 } from "child_process";
52096
+
52097
+ class GitWorktreeWidget {
52098
+ getDefaultColor() {
52099
+ return "blue";
52100
+ }
52101
+ getDescription() {
52102
+ return "Shows the current git worktree name";
52103
+ }
52104
+ getDisplayName() {
52105
+ return "Git Worktree";
52106
+ }
52107
+ getEditorDisplay(item) {
52108
+ const hideNoGit = item.metadata?.hideNoGit === "true";
52109
+ const modifiers = [];
52110
+ if (hideNoGit) {
52111
+ modifiers.push("hide 'no git'");
52112
+ }
52113
+ return {
52114
+ displayText: this.getDisplayName(),
52115
+ modifierText: modifiers.length > 0 ? `(${modifiers.join(", ")})` : undefined
52116
+ };
52117
+ }
52118
+ handleEditorAction(action, item) {
52119
+ if (action === "toggle-nogit") {
52120
+ const currentState = item.metadata?.hideNoGit === "true";
52121
+ return {
52122
+ ...item,
52123
+ metadata: {
52124
+ ...item.metadata,
52125
+ hideNoGit: (!currentState).toString()
52126
+ }
52127
+ };
52128
+ }
52129
+ return null;
52130
+ }
52131
+ render(item, context) {
52132
+ const hideNoGit = item.metadata?.hideNoGit === "true";
52133
+ if (context.isPreview)
52134
+ return item.rawValue ? "main" : "\uD81A\uDC30 main";
52135
+ const worktree = this.getGitWorktree();
52136
+ if (worktree)
52137
+ return item.rawValue ? worktree : `\uD81A\uDC30 ${worktree}`;
52138
+ return hideNoGit ? null : "\uD81A\uDC30 no git";
52139
+ }
52140
+ getGitWorktree() {
52141
+ try {
52142
+ const worktreeDir = execSync6("git rev-parse --git-dir", {
52143
+ encoding: "utf8",
52144
+ stdio: ["pipe", "pipe", "ignore"]
52145
+ }).trim();
52146
+ if (worktreeDir.endsWith("/.git") || worktreeDir === ".git")
52147
+ return "main";
52148
+ const [, worktree] = worktreeDir.split(".git/worktrees/");
52149
+ return worktree ?? null;
52150
+ } catch {
52151
+ return null;
52152
+ }
52153
+ }
52154
+ getCustomKeybinds() {
52155
+ return [
52156
+ { key: "h", label: "(h)ide 'no git' message", action: "toggle-nogit" }
52157
+ ];
52158
+ }
52159
+ supportsRawValue() {
52160
+ return true;
52161
+ }
52162
+ supportsColors(item) {
52163
+ return true;
52164
+ }
52165
+ }
52032
52166
  // src/utils/renderer.ts
52033
52167
  var ANSI_REGEX = new RegExp(`\\x1b\\[[0-9;]*m`, "g");
52034
52168
  function formatTokens(count) {
@@ -52038,7 +52172,7 @@ function formatTokens(count) {
52038
52172
  return `${(count / 1000).toFixed(1)}k`;
52039
52173
  return count.toString();
52040
52174
  }
52041
- function renderPowerlineStatusLine(widgets, settings, context, lineIndex = 0, globalSeparatorOffset = 0, allLinesWidgets) {
52175
+ function renderPowerlineStatusLine(widgets, settings, context, lineIndex = 0, globalSeparatorOffset = 0, preRenderedWidgets, preCalculatedMaxWidths) {
52042
52176
  const powerlineConfig = settings.powerline;
52043
52177
  const config2 = powerlineConfig ?? {};
52044
52178
  const separators = config2.separators ?? [""];
@@ -52092,6 +52226,13 @@ function renderPowerlineStatusLine(widgets, settings, context, lineIndex = 0, gl
52092
52226
  }
52093
52227
  const widgetElements = [];
52094
52228
  let widgetColorIndex = 0;
52229
+ const preRenderedIndices = [];
52230
+ for (let i = 0;i < widgets.length; i++) {
52231
+ const widget = widgets[i];
52232
+ if (widget && widget.type !== "separator" && widget.type !== "flex-separator") {
52233
+ preRenderedIndices.push(i);
52234
+ }
52235
+ }
52095
52236
  for (let i = 0;i < filteredWidgets.length; i++) {
52096
52237
  const widget = filteredWidgets[i];
52097
52238
  if (!widget)
@@ -52101,12 +52242,14 @@ function renderPowerlineStatusLine(widgets, settings, context, lineIndex = 0, gl
52101
52242
  if (widget.type === "separator" || widget.type === "flex-separator") {
52102
52243
  continue;
52103
52244
  }
52104
- try {
52245
+ const actualPreRenderedIndex = preRenderedIndices[i];
52246
+ const preRendered = actualPreRenderedIndex !== undefined ? preRenderedWidgets[actualPreRenderedIndex] : undefined;
52247
+ if (preRendered?.content) {
52248
+ widgetText = preRendered.content;
52105
52249
  const widgetImpl = getWidget(widget.type);
52106
- widgetText = widgetImpl.render(widget, context, settings) ?? "";
52107
- defaultColor = widgetImpl.getDefaultColor();
52108
- } catch {
52109
- continue;
52250
+ if (widgetImpl) {
52251
+ defaultColor = widgetImpl.getDefaultColor();
52252
+ }
52110
52253
  }
52111
52254
  if (widgetText) {
52112
52255
  const padding = settings.defaultPadding ?? "";
@@ -52146,49 +52289,12 @@ function renderPowerlineStatusLine(widgets, settings, context, lineIndex = 0, gl
52146
52289
  if (widgetElements.length === 0)
52147
52290
  return "";
52148
52291
  const autoAlign = config2.autoAlign;
52149
- if (autoAlign && allLinesWidgets) {
52150
- const maxWidths = [];
52151
- const allLinesElements = [];
52152
- for (const lineWidgets of allLinesWidgets) {
52153
- const filteredLineWidgets = lineWidgets.filter((w) => w.type !== "separator" && w.type !== "flex-separator");
52154
- const lineElements = [];
52155
- for (const widget of filteredLineWidgets) {
52156
- let widgetText = "";
52157
- try {
52158
- const widgetImpl = getWidget(widget.type);
52159
- widgetText = widgetImpl.render(widget, context, settings) ?? "";
52160
- } catch {
52161
- continue;
52162
- }
52163
- if (widgetText) {
52164
- const padding = settings.defaultPadding ?? "";
52165
- const paddedText = `${padding}${widgetText}${padding}`;
52166
- lineElements.push({
52167
- content: paddedText,
52168
- bgColor: widget.backgroundColor,
52169
- fgColor: widget.color,
52170
- widget
52171
- });
52172
- }
52173
- }
52174
- allLinesElements.push(lineElements);
52175
- }
52176
- for (let pos = 0;pos < Math.max(...allLinesElements.map((line) => line.length)); pos++) {
52177
- let maxWidth = 0;
52178
- for (const lineElements of allLinesElements) {
52179
- const element = lineElements[pos];
52180
- if (element) {
52181
- const plainLength = element.content.replace(ANSI_REGEX, "").length;
52182
- maxWidth = Math.max(maxWidth, plainLength);
52183
- }
52184
- }
52185
- maxWidths.push(maxWidth);
52186
- }
52292
+ if (autoAlign) {
52187
52293
  for (let i = 0;i < widgetElements.length; i++) {
52188
52294
  const element = widgetElements[i];
52189
- const maxWidth = maxWidths[i];
52295
+ const maxWidth = preCalculatedMaxWidths[i];
52190
52296
  if (element && maxWidth !== undefined) {
52191
- const currentLength = element.content.replace(ANSI_REGEX, "").length;
52297
+ const currentLength = stringWidth(element.content.replace(ANSI_REGEX, ""));
52192
52298
  const paddingNeeded = maxWidth - currentLength;
52193
52299
  if (paddingNeeded > 0) {
52194
52300
  element.content += " ".repeat(paddingNeeded);
@@ -52356,17 +52462,67 @@ function formatSeparator(sep) {
52356
52462
  }
52357
52463
  return sep;
52358
52464
  }
52359
- function renderStatusLineWithInfo(widgets, settings, context, allLinesWidgets) {
52360
- const line = renderStatusLine(widgets, settings, context, allLinesWidgets);
52465
+ function preRenderAllWidgets(allLinesWidgets, settings, context) {
52466
+ const preRenderedLines = [];
52467
+ for (const lineWidgets of allLinesWidgets) {
52468
+ const preRenderedLine = [];
52469
+ for (const widget of lineWidgets) {
52470
+ if (widget.type === "separator" || widget.type === "flex-separator") {
52471
+ preRenderedLine.push({
52472
+ content: "",
52473
+ plainLength: 0,
52474
+ widget
52475
+ });
52476
+ continue;
52477
+ }
52478
+ const widgetImpl = getWidget(widget.type);
52479
+ if (!widgetImpl) {
52480
+ continue;
52481
+ }
52482
+ const widgetText = widgetImpl.render(widget, context, settings) ?? "";
52483
+ const plainLength = stringWidth(widgetText.replace(ANSI_REGEX, ""));
52484
+ preRenderedLine.push({
52485
+ content: widgetText,
52486
+ plainLength,
52487
+ widget
52488
+ });
52489
+ }
52490
+ preRenderedLines.push(preRenderedLine);
52491
+ }
52492
+ return preRenderedLines;
52493
+ }
52494
+ function calculateMaxWidthsFromPreRendered(preRenderedLines, settings) {
52495
+ const maxWidths = [];
52496
+ const defaultPadding = settings.defaultPadding ?? "";
52497
+ const paddingLength = defaultPadding.length;
52498
+ for (const preRenderedLine of preRenderedLines) {
52499
+ const filteredWidgets = preRenderedLine.filter((w) => w.widget.type !== "separator" && w.widget.type !== "flex-separator" && w.content);
52500
+ for (let pos = 0;pos < filteredWidgets.length; pos++) {
52501
+ const widget = filteredWidgets[pos];
52502
+ if (!widget)
52503
+ continue;
52504
+ const totalWidth = widget.plainLength + paddingLength * 2;
52505
+ const currentMax = maxWidths[pos];
52506
+ if (currentMax === undefined) {
52507
+ maxWidths[pos] = totalWidth;
52508
+ } else {
52509
+ maxWidths[pos] = Math.max(currentMax, totalWidth);
52510
+ }
52511
+ }
52512
+ }
52513
+ return maxWidths;
52514
+ }
52515
+ function renderStatusLineWithInfo(widgets, settings, context, preRenderedWidgets, preCalculatedMaxWidths) {
52516
+ const line = renderStatusLine(widgets, settings, context, preRenderedWidgets, preCalculatedMaxWidths);
52361
52517
  const wasTruncated = line.includes("...");
52362
52518
  return { line, wasTruncated };
52363
52519
  }
52364
- function renderStatusLine(widgets, settings, context, allLinesWidgets) {
52520
+ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCalculatedMaxWidths) {
52365
52521
  const colorLevel = getColorLevelString(settings.colorLevel);
52366
52522
  const powerlineSettings = settings.powerline;
52367
52523
  const isPowerlineMode = Boolean(powerlineSettings?.enabled);
52368
52524
  if (isPowerlineMode)
52369
- return renderPowerlineStatusLine(widgets, settings, context, context.lineIndex ?? 0, context.globalSeparatorIndex ?? 0, allLinesWidgets);
52525
+ return renderPowerlineStatusLine(widgets, settings, context, context.lineIndex ?? 0, context.globalSeparatorIndex ?? 0, preRenderedWidgets, preCalculatedMaxWidths);
52370
52526
  const applyColorsWithOverride = (text, foregroundColor, backgroundColor, bold) => {
52371
52527
  let fgColor = foregroundColor;
52372
52528
  if (settings.overrideForegroundColor && settings.overrideForegroundColor !== "none") {
@@ -52424,12 +52580,8 @@ function renderStatusLine(widgets, settings, context, allLinesWidgets) {
52424
52580
  if (prevWidget && prevWidget.type !== "separator" && prevWidget.type !== "flex-separator") {
52425
52581
  let widgetColor = prevWidget.color;
52426
52582
  if (!widgetColor) {
52427
- try {
52428
- const widgetImpl = getWidget(prevWidget.type);
52429
- widgetColor = widgetImpl.getDefaultColor();
52430
- } catch {
52431
- widgetColor = "white";
52432
- }
52583
+ const widgetImpl = getWidget(prevWidget.type);
52584
+ widgetColor = widgetImpl ? widgetImpl.getDefaultColor() : "white";
52433
52585
  }
52434
52586
  separatorColor = widgetColor;
52435
52587
  separatorBg = prevWidget.backgroundColor;
@@ -52445,10 +52597,17 @@ function renderStatusLine(widgets, settings, context, allLinesWidgets) {
52445
52597
  continue;
52446
52598
  }
52447
52599
  try {
52448
- const widgetImpl = getWidget(widget.type);
52449
- const widgetText = widgetImpl.render(widget, context, settings);
52600
+ let widgetText;
52601
+ let defaultColor = "white";
52602
+ const preRendered = preRenderedWidgets[i];
52603
+ if (preRendered?.content) {
52604
+ widgetText = preRendered.content;
52605
+ const widgetImpl = getWidget(widget.type);
52606
+ if (widgetImpl) {
52607
+ defaultColor = widgetImpl.getDefaultColor();
52608
+ }
52609
+ }
52450
52610
  if (widgetText) {
52451
- const defaultColor = widgetImpl.getDefaultColor();
52452
52611
  if (widget.type === "custom-command" && widget.preserveColors) {
52453
52612
  let finalOutput = widgetText;
52454
52613
  if (widget.maxWidth && widget.maxWidth > 0) {
@@ -52512,7 +52671,7 @@ function renderStatusLine(widgets, settings, context, allLinesWidgets) {
52512
52671
  let widgetColor = prevElem2.widget.color;
52513
52672
  if (!widgetColor && prevElem2.widget.type !== "separator" && prevElem2.widget.type !== "flex-separator") {
52514
52673
  const widgetImpl = getWidget(prevElem2.widget.type);
52515
- widgetColor = widgetImpl.getDefaultColor();
52674
+ widgetColor = widgetImpl ? widgetImpl.getDefaultColor() : "white";
52516
52675
  }
52517
52676
  const coloredSep = applyColorsWithOverride(defaultSep, widgetColor, prevElem2.widget.backgroundColor, prevElem2.widget.bold);
52518
52677
  finalElements.push(coloredSep);
@@ -53042,7 +53201,7 @@ var CustomTextEditor = ({ widget, onComplete, onCancel }) => {
53042
53201
  }, undefined, true, undefined, this);
53043
53202
  };
53044
53203
  // src/widgets/CustomCommand.tsx
53045
- import { execSync as execSync6 } from "child_process";
53204
+ import { execSync as execSync7 } from "child_process";
53046
53205
  var import_react30 = __toESM(require_react(), 1);
53047
53206
  var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
53048
53207
 
@@ -53088,7 +53247,7 @@ class CustomCommandWidget {
53088
53247
  try {
53089
53248
  const timeout = item.timeout ?? 1000;
53090
53249
  const jsonInput = JSON.stringify(context.data);
53091
- let output = execSync6(item.commandPath, {
53250
+ let output = execSync7(item.commandPath, {
53092
53251
  encoding: "utf8",
53093
53252
  input: jsonInput,
53094
53253
  timeout,
@@ -53543,6 +53702,7 @@ var widgetRegistry = new Map([
53543
53702
  ["output-style", new OutputStyleWidget],
53544
53703
  ["git-branch", new GitBranchWidget],
53545
53704
  ["git-changes", new GitChangesWidget],
53705
+ ["git-worktree", new GitWorktreeWidget],
53546
53706
  ["current-working-dir", new CurrentWorkingDirWidget],
53547
53707
  ["tokens-input", new TokensInputWidget],
53548
53708
  ["tokens-output", new TokensOutputWidget],
@@ -53560,11 +53720,7 @@ var widgetRegistry = new Map([
53560
53720
  ["custom-command", new CustomCommandWidget]
53561
53721
  ]);
53562
53722
  function getWidget(type) {
53563
- const widget = widgetRegistry.get(type);
53564
- if (!widget) {
53565
- throw new Error(`Unknown widget type: ${type}`);
53566
- }
53567
- return widget;
53723
+ return widgetRegistry.get(type) ?? null;
53568
53724
  }
53569
53725
  function getAllWidgetTypes(settings) {
53570
53726
  const allTypes = Array.from(widgetRegistry.keys());
@@ -53651,12 +53807,8 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
53651
53807
  if (widget.type === "separator") {
53652
53808
  return showSeparators;
53653
53809
  }
53654
- try {
53655
- const widgetInstance = getWidget(widget.type);
53656
- return widgetInstance.supportsColors(widget);
53657
- } catch {
53658
- return false;
53659
- }
53810
+ const widgetInstance = getWidget(widget.type);
53811
+ return widgetInstance ? widgetInstance.supportsColors(widget) : true;
53660
53812
  });
53661
53813
  const [highlightedItemId, setHighlightedItemId] = import_react33.useState(colorableWidgets[0]?.id ?? null);
53662
53814
  const [editingBackground, setEditingBackground] = import_react33.useState(false);
@@ -53826,7 +53978,9 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
53826
53978
  let defaultColor = "white";
53827
53979
  if (widget.type !== "separator" && widget.type !== "flex-separator") {
53828
53980
  const widgetImpl = getWidget(widget.type);
53829
- defaultColor = widgetImpl.getDefaultColor();
53981
+ if (widgetImpl) {
53982
+ defaultColor = widgetImpl.getDefaultColor();
53983
+ }
53830
53984
  }
53831
53985
  let currentColor2 = widget.color ?? defaultColor;
53832
53986
  if (currentColor2 === "dim") {
@@ -53892,7 +54046,7 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
53892
54046
  return "Flex Separator";
53893
54047
  }
53894
54048
  const widgetImpl = getWidget(widget.type);
53895
- return widgetImpl.getDisplayName();
54049
+ return widgetImpl ? widgetImpl.getDisplayName() : `Unknown: ${widget.type}`;
53896
54050
  };
53897
54051
  const colorOptions = getAvailableColorsForUI();
53898
54052
  const colors = colorOptions.map((c) => c.value || "");
@@ -53904,7 +54058,9 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
53904
54058
  let defaultColor = "white";
53905
54059
  if (widget.type !== "separator" && widget.type !== "flex-separator") {
53906
54060
  const widgetImpl = getWidget(widget.type);
53907
- defaultColor = widgetImpl.getDefaultColor();
54061
+ if (widgetImpl) {
54062
+ defaultColor = widgetImpl.getDefaultColor();
54063
+ }
53908
54064
  }
53909
54065
  const styledLabel = applyColors(label, widget.color ?? defaultColor, widget.backgroundColor, widget.bold, level);
53910
54066
  return {
@@ -53925,7 +54081,7 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
53925
54081
  const currentColor = editingBackground ? selectedWidget?.backgroundColor ?? "" : selectedWidget ? selectedWidget.color ?? (() => {
53926
54082
  if (selectedWidget.type !== "separator" && selectedWidget.type !== "flex-separator") {
53927
54083
  const widgetImpl = getWidget(selectedWidget.type);
53928
- return widgetImpl.getDefaultColor();
54084
+ return widgetImpl ? widgetImpl.getDefaultColor() : "white";
53929
54085
  }
53930
54086
  return "white";
53931
54087
  })() : "white";
@@ -54872,8 +55028,8 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
54872
55028
  } else if (widgets.length > 0) {
54873
55029
  const currentWidget2 = widgets[selectedIndex];
54874
55030
  if (currentWidget2 && currentWidget2.type !== "separator" && currentWidget2.type !== "flex-separator") {
54875
- try {
54876
- const widgetImpl = getWidget(currentWidget2.type);
55031
+ const widgetImpl = getWidget(currentWidget2.type);
55032
+ if (widgetImpl) {
54877
55033
  if (widgetImpl.getCustomKeybinds) {
54878
55034
  const customKeybinds2 = widgetImpl.getCustomKeybinds();
54879
55035
  const matchedKeybind = customKeybinds2.find((kb) => kb.key === input);
@@ -54892,7 +55048,7 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
54892
55048
  }
54893
55049
  }
54894
55050
  }
54895
- } catch {}
55051
+ }
54896
55052
  }
54897
55053
  }
54898
55054
  }
@@ -54907,8 +55063,11 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
54907
55063
  return "Flex Separator";
54908
55064
  }
54909
55065
  const widgetImpl = getWidget(widget.type);
54910
- const { displayText, modifierText } = widgetImpl.getEditorDisplay(widget);
54911
- return displayText + (modifierText ? ` ${modifierText}` : "");
55066
+ if (widgetImpl) {
55067
+ const { displayText, modifierText } = widgetImpl.getEditorDisplay(widget);
55068
+ return displayText + (modifierText ? ` ${modifierText}` : "");
55069
+ }
55070
+ return `Unknown: ${widget.type}`;
54912
55071
  };
54913
55072
  const hasFlexSeparator = widgets.some((widget) => widget.type === "flex-separator");
54914
55073
  const widthDetectionAvailable = canDetectTerminalWidth();
@@ -54918,13 +55077,13 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
54918
55077
  let canToggleRaw = false;
54919
55078
  let customKeybinds = [];
54920
55079
  if (currentWidget && !isSeparator && !isFlexSeparator) {
54921
- try {
54922
- const widgetImpl = getWidget(currentWidget.type);
55080
+ const widgetImpl = getWidget(currentWidget.type);
55081
+ if (widgetImpl) {
54923
55082
  canToggleRaw = widgetImpl.supportsRawValue();
54924
55083
  if (widgetImpl.getCustomKeybinds) {
54925
55084
  customKeybinds = widgetImpl.getCustomKeybinds();
54926
55085
  }
54927
- } catch {
55086
+ } else {
54928
55087
  canToggleRaw = false;
54929
55088
  }
54930
55089
  }
@@ -55074,12 +55233,8 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
55074
55233
  } else if (currentWidget.type === "flex-separator") {
55075
55234
  return "Expands to fill available terminal width";
55076
55235
  } else {
55077
- try {
55078
- const widgetImpl = getWidget(currentWidget.type);
55079
- return widgetImpl.getDescription();
55080
- } catch {
55081
- return "Widget description not available";
55082
- }
55236
+ const widgetImpl = getWidget(currentWidget.type);
55237
+ return widgetImpl ? widgetImpl.getDescription() : "Unknown widget type";
55083
55238
  }
55084
55239
  })()
55085
55240
  }, undefined, false, undefined, this)
@@ -56365,30 +56520,30 @@ var PowerlineSetup = ({
56365
56520
  // src/tui/components/StatusLinePreview.tsx
56366
56521
  var import_react42 = __toESM(require_react(), 1);
56367
56522
  var jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
56368
- var renderSingleLine = (widgets, terminalWidth, widthDetectionAvailable, settings, lineIndex, globalSeparatorIndex, allLinesWidgets) => {
56523
+ var renderSingleLine = (widgets, terminalWidth, widthDetectionAvailable, settings, lineIndex, globalSeparatorIndex, preRenderedWidgets, preCalculatedMaxWidths) => {
56369
56524
  const context = {
56370
56525
  terminalWidth,
56371
56526
  isPreview: true,
56372
56527
  lineIndex,
56373
56528
  globalSeparatorIndex
56374
56529
  };
56375
- return renderStatusLineWithInfo(widgets, settings, context, allLinesWidgets);
56530
+ return renderStatusLineWithInfo(widgets, settings, context, preRenderedWidgets, preCalculatedMaxWidths);
56376
56531
  };
56377
56532
  var StatusLinePreview = ({ lines, terminalWidth, settings, onTruncationChange }) => {
56378
56533
  const widthDetectionAvailable = import_react42.default.useMemo(() => canDetectTerminalWidth(), []);
56379
56534
  const { renderedLines, anyTruncated } = import_react42.default.useMemo(() => {
56380
56535
  if (!settings)
56381
56536
  return { renderedLines: [], anyTruncated: false };
56382
- const isPowerlineMode = settings.powerline.enabled;
56383
- const autoAlign = settings.powerline.autoAlign;
56384
- const allLinesWidgets = isPowerlineMode && autoAlign ? lines : undefined;
56537
+ const preRenderedLines = preRenderAllWidgets(lines, settings, { terminalWidth, isPreview: true });
56538
+ const preCalculatedMaxWidths = calculateMaxWidthsFromPreRendered(preRenderedLines, settings);
56385
56539
  let globalSeparatorIndex = 0;
56386
56540
  const result = [];
56387
56541
  let truncated = false;
56388
56542
  for (let i = 0;i < lines.length; i++) {
56389
56543
  const lineItems = lines[i];
56390
56544
  if (lineItems && lineItems.length > 0) {
56391
- const renderResult = renderSingleLine(lineItems, terminalWidth, widthDetectionAvailable, settings, i, globalSeparatorIndex, allLinesWidgets);
56545
+ const preRenderedWidgets = preRenderedLines[i] ?? [];
56546
+ const renderResult = renderSingleLine(lineItems, terminalWidth, widthDetectionAvailable, settings, i, globalSeparatorIndex, preRenderedWidgets, preCalculatedMaxWidths);
56392
56547
  result.push(renderResult.line);
56393
56548
  if (renderResult.wasTruncated) {
56394
56549
  truncated = true;
@@ -56460,7 +56615,9 @@ var TerminalOptionsMenu = ({ settings, onUpdate, onBack }) => {
56460
56615
  if (widget.color?.startsWith("hex:")) {
56461
56616
  if (widget.type !== "separator" && widget.type !== "flex-separator") {
56462
56617
  const widgetImpl = getWidget(widget.type);
56463
- newWidget.color = widgetImpl.getDefaultColor();
56618
+ if (widgetImpl) {
56619
+ newWidget.color = widgetImpl.getDefaultColor();
56620
+ }
56464
56621
  }
56465
56622
  }
56466
56623
  if (widget.backgroundColor?.startsWith("hex:")) {
@@ -56470,7 +56627,9 @@ var TerminalOptionsMenu = ({ settings, onUpdate, onBack }) => {
56470
56627
  if (widget.color?.startsWith("ansi256:")) {
56471
56628
  if (widget.type !== "separator" && widget.type !== "flex-separator") {
56472
56629
  const widgetImpl = getWidget(widget.type);
56473
- newWidget.color = widgetImpl.getDefaultColor();
56630
+ if (widgetImpl) {
56631
+ newWidget.color = widgetImpl.getDefaultColor();
56632
+ }
56474
56633
  }
56475
56634
  }
56476
56635
  if (widget.backgroundColor?.startsWith("ansi256:")) {
@@ -56480,7 +56639,9 @@ var TerminalOptionsMenu = ({ settings, onUpdate, onBack }) => {
56480
56639
  if (widget.color?.startsWith("ansi256:") || widget.color?.startsWith("hex:")) {
56481
56640
  if (widget.type !== "separator" && widget.type !== "flex-separator") {
56482
56641
  const widgetImpl = getWidget(widget.type);
56483
- newWidget.color = widgetImpl.getDefaultColor();
56642
+ if (widgetImpl) {
56643
+ newWidget.color = widgetImpl.getDefaultColor();
56644
+ }
56484
56645
  }
56485
56646
  }
56486
56647
  if (widget.backgroundColor?.startsWith("ansi256:") || widget.backgroundColor?.startsWith("hex:")) {
@@ -56506,7 +56667,9 @@ var TerminalOptionsMenu = ({ settings, onUpdate, onBack }) => {
56506
56667
  if (widget.color?.startsWith("ansi256:") || widget.color?.startsWith("hex:")) {
56507
56668
  if (widget.type !== "separator" && widget.type !== "flex-separator") {
56508
56669
  const widgetImpl = getWidget(widget.type);
56509
- newWidget.color = widgetImpl.getDefaultColor();
56670
+ if (widgetImpl) {
56671
+ newWidget.color = widgetImpl.getDefaultColor();
56672
+ }
56510
56673
  }
56511
56674
  }
56512
56675
  if (widget.backgroundColor?.startsWith("ansi256:") || widget.backgroundColor?.startsWith("hex:")) {
@@ -58330,15 +58493,15 @@ async function renderMultipleLines(data) {
58330
58493
  blockMetrics,
58331
58494
  isPreview: false
58332
58495
  };
58333
- const isPowerlineMode = settings.powerline.enabled;
58334
- const autoAlign = settings.powerline.autoAlign;
58335
- const allLinesWidgets = isPowerlineMode && autoAlign ? lines : undefined;
58496
+ const preRenderedLines = preRenderAllWidgets(lines, settings, context);
58497
+ const preCalculatedMaxWidths = calculateMaxWidthsFromPreRendered(preRenderedLines, settings);
58336
58498
  let globalSeparatorIndex = 0;
58337
58499
  for (let i = 0;i < lines.length; i++) {
58338
58500
  const lineItems = lines[i];
58339
58501
  if (lineItems && lineItems.length > 0) {
58340
58502
  const lineContext = { ...context, lineIndex: i, globalSeparatorIndex };
58341
- const line = renderStatusLine(lineItems, settings, lineContext, allLinesWidgets);
58503
+ const preRenderedWidgets = preRenderedLines[i] ?? [];
58504
+ const line = renderStatusLine(lineItems, settings, lineContext, preRenderedWidgets, preCalculatedMaxWidths);
58342
58505
  const strippedLine = line.replace(/\x1b\[[0-9;]*m/g, "").trim();
58343
58506
  if (strippedLine.length > 0) {
58344
58507
  const nonMergedWidgets = lineItems.filter((_, idx) => idx === lineItems.length - 1 || !lineItems[idx]?.merge);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline",
3
- "version": "2.0.8",
3
+ "version": "2.0.10",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",
@@ -11,13 +11,13 @@
11
11
  "dist/"
12
12
  ],
13
13
  "scripts": {
14
- "start": "bun run patch && bun run src/ccstatusline.ts",
14
+ "start": "bun run src/ccstatusline.ts",
15
15
  "statusline": "bun run src/ccstatusline.ts",
16
- "patch": "patch-package",
17
- "build": "bun run patch && rm -rf dist/* && bun build src/ccstatusline.ts --target=node --outfile=dist/ccstatusline.js --target-version=14",
16
+ "build": "rm -rf dist/* && bun build src/ccstatusline.ts --target=node --outfile=dist/ccstatusline.js --target-version=14",
18
17
  "postbuild": "bun run scripts/replace-version.ts",
19
18
  "prepublishOnly": "bun run build",
20
- "lint": "bun tsc --noEmit; eslint . --config eslint.config.js --max-warnings=999999 --fix"
19
+ "lint": "bun tsc --noEmit; eslint . --config eslint.config.js --max-warnings=999999 --fix",
20
+ "test": "bun vitest"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@eslint/js": "^9.33.0",
@@ -34,13 +34,13 @@
34
34
  "ink": "^6.2.0",
35
35
  "ink-gradient": "^3.0.0",
36
36
  "ink-select-input": "^6.2.0",
37
- "patch-package": "^8.0.0",
38
37
  "react": "^19.1.1",
39
38
  "react-devtools-core": "^6.1.5",
40
39
  "strip-ansi": "^7.1.0",
41
40
  "tinyglobby": "^0.2.14",
42
41
  "typescript": "^5.9.2",
43
42
  "typescript-eslint": "^8.39.1",
43
+ "vitest": "^3.2.4",
44
44
  "zod": "^4.0.17"
45
45
  },
46
46
  "keywords": [
@@ -61,5 +61,8 @@
61
61
  },
62
62
  "trustedDependencies": [
63
63
  "unrs-resolver"
64
- ]
64
+ ],
65
+ "patchedDependencies": {
66
+ "ink@6.2.0": "patches/ink@6.2.0.patch"
67
+ }
65
68
  }