ccstatusline-usage 2.3.14 → 2.3.16

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
@@ -32,7 +32,7 @@ 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
34
  - **Weekly Pace** - Pendulum bar showing if you're ahead or behind expected usage pace
35
- - **Reset Timer** - Time until 5-hour session window resets
35
+ - **Reset Timer** - Time until weekly reset (when at 100% / on a charged model); otherwise time until 5-hour session window resets
36
36
  - **Context Window Display** - Visual bar showing context usage
37
37
  - **Off Peak** - Shows peak/off-peak status with countdown timer (peak hours drain sessions faster)
38
38
  - **Two-line Layout** - Session info on line 1, context on line 2
@@ -40,7 +40,7 @@ This fork adds API-based usage widgets beyond the upstream:
40
40
  ### Enhanced Status Line Preview
41
41
 
42
42
  ```
43
- Session: [████░░░░░░░░░░░] 27.0% | Weekly: [████░░░░░░░░░░░] 34.0% | 2:03 hr | Model: Opus 4.6 | Session ID: 0109b99d...
43
+ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [████░░░░░░░░░░░] 34.0% | 2:03 hr | Model: Opus 4.7 | Session ID: 0109b99d...
44
44
  Context: [██████░░░░░░░░░] 389k/1M (39%) | Pace: [░░░░░░█|░░░░░░░] D4/7 -8% | Off-peak (4:03 hr)
45
45
  ```
46
46
 
@@ -67,6 +67,17 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
67
67
 
68
68
  ## 🆕 Recent Updates
69
69
 
70
+ ### [v2.3.16](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.16) - Reset Timer fix + model ref updates + upstream sync
71
+
72
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Reset Timer fix** — When session hits 100% but weekly is still below 100%, the Reset Timer now correctly shows the session reset countdown (5-hour window) instead of the weekly reset time. Previously it jumped to the weekly timer as soon as session filled up.
73
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): Updated all Opus 4.6 references to Opus 4.7 throughout docs, examples, and test data.
74
+ - Upstream sync (5 commits): refreshInterval configuration for Claude Code status line (#297), xhigh thinking effort level (#314), dev-dependency bumps.
75
+
76
+ ### [v2.3.15](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.15) - Move extra amounts into Weekly bar, Reset Timer shows weekly reset time
77
+
78
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Extra amounts in Weekly bar** — When at 100% / on a charged model, the split bar now shows `€spent/€limit` after the bar instead of `100.0%`. Mobile shows the spent amount only.
79
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Reset Timer shows weekly reset time** — When extra usage is active the timer now counts down to the weekly reset instead of showing the `Extra: €X/€Y` spending (which moved to the Weekly bar). Falls back to session timer if no weekly reset data is available.
80
+
70
81
  ### [v2.3.14](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.14) - Drop extraUsageBalance setting
71
82
 
72
83
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): Removed the `extraUsageBalance` setting introduced in v2.3.12. `Extra: €X/€Y` now uses the API's monthly limit directly — stable, no config, no drift.
@@ -150,7 +161,7 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
150
161
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **1M context indicator** — Model widget now appends `[1m]` when a 1M context model is active, both in full (`Model: claude-sonnet-4-6 [1m]`) and compact (`M: s4.6[1m]`) display modes
151
162
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Revert extra usage on 1M** — Removed the v2.1.15 trigger that showed extra usage spending on 1M models. 1M context is not included in Max without extra usage — the `[1m]` suffix on the Model widget now makes this visible, so the Reset Timer no longer needs to duplicate that awareness
152
163
 
153
- ### [v2.1.15](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.15) - Show extra usage balance on 1M context models
164
+ ### [v2.1.15](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.15) - Show extra usage Kelp on 1M context models
154
165
 
155
166
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Extra usage on 1M models** — Reset Timer widget now shows extra usage spending (`Extra: €X.XX/€Y.YY`) whenever a 1M context model is active (Opus/Sonnet with 1M context window), in addition to the existing trigger when weekly limit reaches 100%. *Reverted in v2.1.16.*
156
167
 
@@ -169,9 +180,9 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
169
180
 
170
181
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Remove thinking effort bars** — Claude Code now shows thinking intensity natively in its own UI, so the `▌▌▌` bars after the model name have been removed from the Model widget
171
182
 
172
- ### [v2.1.11](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.11) - Configurable extra usage balance
183
+ ### [v2.1.11](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.11) - Configurable extra usage Kelp
173
184
 
174
- - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Configurable extra usage balance** — Add `extraUsageBalance` setting (in cents) to `~/.config/ccstatusline/settings.json` to override the API's monthly limit with your actual prepaid balance (e.g., `"extraUsageBalance": 5000` shows `Extra: €0.00/€50.00`)
185
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Configurable extra usage Kelp** — Add `extraUsageBalance` setting (in cents) to `~/.config/ccstatusline/settings.json` to override the API's monthly limit with your actual prepaid Kelp (e.g., `"extraUsageBalance": 5000` shows `Extra: €0.00/€50.00`)
175
186
 
176
187
  ### [v2.1.8](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.8) - Windows support for API usage widgets
177
188
 
@@ -55592,7 +55592,7 @@ function getTerminalWidth() {
55592
55592
  function canDetectTerminalWidth() {
55593
55593
  return probeTerminalWidth() !== null;
55594
55594
  }
55595
- var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.14";
55595
+ var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.16";
55596
55596
  var init_terminal = () => {};
55597
55597
 
55598
55598
  // src/utils/renderer.ts
@@ -61996,8 +61996,11 @@ function normalizeThinkingEffort(value) {
61996
61996
  return;
61997
61997
  }
61998
61998
  const normalized = value.toLowerCase();
61999
- if (normalized === "low" || normalized === "medium" || normalized === "high" || normalized === "max") {
62000
- return normalized;
61999
+ if (KNOWN_THINKING_EFFORTS_SET.has(normalized)) {
62000
+ return { value: normalized, known: true };
62001
+ }
62002
+ if (UNKNOWN_EFFORT_PATTERN.test(normalized)) {
62003
+ return { value: normalized, known: false };
62001
62004
  }
62002
62005
  return;
62003
62006
  }
@@ -62028,11 +62031,14 @@ function getTranscriptThinkingEffort(transcriptPath) {
62028
62031
  }
62029
62032
  return;
62030
62033
  }
62031
- var MODEL_STDOUT_PREFIX = "<local-command-stdout>Set model to ", MODEL_STDOUT_EFFORT_REGEX;
62034
+ var KNOWN_THINKING_EFFORTS, KNOWN_THINKING_EFFORTS_SET, MODEL_STDOUT_PREFIX = "<local-command-stdout>Set model to ", MODEL_STDOUT_EFFORT_REGEX, UNKNOWN_EFFORT_PATTERN;
62032
62035
  var init_jsonl_metadata = __esm(() => {
62033
62036
  init_ansi();
62034
62037
  init_jsonl_lines();
62035
- MODEL_STDOUT_EFFORT_REGEX = /^<local-command-stdout>Set model to[\s\S]*? with (low|medium|high|max) effort<\/local-command-stdout>$/i;
62038
+ KNOWN_THINKING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
62039
+ KNOWN_THINKING_EFFORTS_SET = new Set(KNOWN_THINKING_EFFORTS);
62040
+ MODEL_STDOUT_EFFORT_REGEX = /^<local-command-stdout>Set model to[\s\S]*? with ([a-zA-Z0-9-]+) effort<\/local-command-stdout>$/i;
62041
+ UNKNOWN_EFFORT_PATTERN = /^(?=.*[a-z0-9])[a-z0-9-]{2,20}$/;
62036
62042
  });
62037
62043
 
62038
62044
  // src/utils/jsonl.ts
@@ -63157,9 +63163,16 @@ function formatUsageBar(label, shortLabel, percent, size2) {
63157
63163
  const display = Math.min(100, percent);
63158
63164
  return `${size2 === "mobile" ? shortLabel : label}: ${bar} ${display.toFixed(1)}%`;
63159
63165
  }
63160
- function formatSplitUsageBar(label, shortLabel, extraPercent, size2) {
63166
+ function formatSplitUsageBar(label, shortLabel, extraPercent, size2, extraUsed, extraLimit) {
63167
+ const displayLabel = size2 === "mobile" ? shortLabel : label;
63168
+ const suffix = extraUsed !== undefined && extraLimit !== undefined ? `${DARK_RED_OPEN2}${formatCents(extraUsed)}/${formatCents(extraLimit)}${DARK_RED_CLOSE2}` : "100.0%";
63169
+ if (size2 === "mobile") {
63170
+ const bar2 = makeProgressBar(100, getBarWidth(size2));
63171
+ const mobileSuffix = extraUsed !== undefined ? `${DARK_RED_OPEN2}${formatCents(extraUsed)}${DARK_RED_CLOSE2}` : suffix;
63172
+ return `${displayLabel}: ${bar2} ${mobileSuffix}`;
63173
+ }
63161
63174
  const bar = makeSplitUsageBar(extraPercent, getBarWidth(size2));
63162
- return `${size2 === "mobile" ? shortLabel : label}: ${bar} 100.0%`;
63175
+ return `${displayLabel}: ${bar} ${suffix}`;
63163
63176
  }
63164
63177
  function computeExtraPercent(extraUsed, extraLimit) {
63165
63178
  return extraLimit > 0 ? extraUsed / extraLimit * 100 : 0;
@@ -63245,9 +63258,9 @@ class WeeklyUsageWidget {
63245
63258
  const size2 = getDisplaySize(context);
63246
63259
  const extraUsed = data.extraUsageUsed;
63247
63260
  const extraLimit = data.extraUsageLimit;
63248
- if (size2 !== "mobile" && data.extraUsageEnabled === true && extraUsed !== undefined && extraLimit !== undefined && data.weeklyUsage >= 100) {
63261
+ if (data.extraUsageEnabled === true && extraUsed !== undefined && extraLimit !== undefined && data.weeklyUsage >= 100) {
63249
63262
  const extraPercent = computeExtraPercent(extraUsed, extraLimit);
63250
- return formatSplitUsageBar("Weekly", "W", extraPercent, size2);
63263
+ return formatSplitUsageBar("Weekly", "W", extraPercent, size2, extraUsed, extraLimit);
63251
63264
  }
63252
63265
  return formatUsageBar("Weekly", "W", data.weeklyUsage, size2);
63253
63266
  }
@@ -63286,10 +63299,14 @@ class ResetTimerWidget {
63286
63299
  const is1mModel = modelId.includes("[1m]");
63287
63300
  const isOpus = modelId.includes("opus");
63288
63301
  const isChargedModel = is1mModel && !isOpus;
63289
- if (data.extraUsageEnabled && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined && (data.weeklyUsage !== undefined && data.weeklyUsage >= 100 || data.sessionUsage !== undefined && data.sessionUsage >= 100 || isChargedModel)) {
63290
- const used = formatCents(data.extraUsageUsed);
63291
- const limit = formatCents(data.extraUsageLimit);
63292
- return `${DARK_RED_OPEN2}Extra: ${used}/${limit}${DARK_RED_CLOSE2}`;
63302
+ const extraActive = data.extraUsageEnabled && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined && (data.weeklyUsage !== undefined && data.weeklyUsage >= 100 || isChargedModel);
63303
+ if (extraActive) {
63304
+ const weeklyWindow = resolveWeeklyUsageWindow(data);
63305
+ if (weeklyWindow) {
63306
+ const hours = Math.floor(weeklyWindow.remainingMs / (1000 * 60 * 60));
63307
+ const minutes = Math.floor(weeklyWindow.remainingMs % (1000 * 60 * 60) / (1000 * 60));
63308
+ return `${hours}:${minutes.toString().padStart(2, "0")} hr`;
63309
+ }
63293
63310
  }
63294
63311
  if (!data.sessionResetAt)
63295
63312
  return null;
@@ -63883,25 +63900,21 @@ var init_Skills = __esm(async () => {
63883
63900
  });
63884
63901
 
63885
63902
  // src/widgets/ThinkingEffort.ts
63886
- function normalizeThinkingEffort2(value) {
63887
- if (!value) {
63888
- return;
63889
- }
63890
- const normalized = value.toLowerCase();
63891
- if (normalized === "low" || normalized === "medium" || normalized === "high" || normalized === "max") {
63892
- return normalized;
63893
- }
63894
- return;
63895
- }
63896
63903
  function resolveThinkingEffortFromSettings() {
63897
63904
  try {
63898
63905
  const settings = loadClaudeSettingsSync({ logErrors: false });
63899
- return normalizeThinkingEffort2(settings.effortLevel);
63906
+ return normalizeThinkingEffort(settings.effortLevel);
63900
63907
  } catch {}
63901
63908
  return;
63902
63909
  }
63903
63910
  function resolveThinkingEffort(context) {
63904
- return getTranscriptThinkingEffort(context.data?.transcript_path) ?? resolveThinkingEffortFromSettings() ?? "medium";
63911
+ return getTranscriptThinkingEffort(context.data?.transcript_path) ?? resolveThinkingEffortFromSettings() ?? null;
63912
+ }
63913
+ function formatEffort(resolved) {
63914
+ if (!resolved) {
63915
+ return "default";
63916
+ }
63917
+ return resolved.known ? resolved.value : `${resolved.value}?`;
63905
63918
  }
63906
63919
 
63907
63920
  class ThinkingEffortWidget {
@@ -63909,7 +63922,8 @@ class ThinkingEffortWidget {
63909
63922
  return "magenta";
63910
63923
  }
63911
63924
  getDescription() {
63912
- return `Displays the current thinking effort level (low, medium, high, max).
63925
+ return `Displays the current thinking effort level (low, medium, high, xhigh, max).
63926
+ Unknown levels are shown with a trailing "?" (e.g. "super-max?").
63913
63927
  May be incorrect when multiple Claude Code sessions are running due to current Claude Code limitations.`;
63914
63928
  }
63915
63929
  getDisplayName() {
@@ -63925,7 +63939,7 @@ May be incorrect when multiple Claude Code sessions are running due to current C
63925
63939
  if (context.isPreview) {
63926
63940
  return item.rawValue ? "high" : "Thinking: high";
63927
63941
  }
63928
- const effort = resolveThinkingEffort(context);
63942
+ const effort = formatEffort(resolveThinkingEffort(context));
63929
63943
  return item.rawValue ? effort : `Thinking: ${effort}`;
63930
63944
  }
63931
63945
  supportsRawValue() {
@@ -65076,7 +65090,7 @@ import * as os9 from "os";
65076
65090
  import * as path10 from "path";
65077
65091
  function isKnownCommand(command) {
65078
65092
  const prefixes = [CCSTATUSLINE_COMMANDS.NPM, CCSTATUSLINE_COMMANDS.BUNX, CCSTATUSLINE_COMMANDS.SELF_MANAGED];
65079
- return prefixes.some((prefix) => command === prefix || command.startsWith(`${prefix} --config `));
65093
+ return prefixes.some((prefix) => command === prefix || command.startsWith(`${prefix} --config `)) || /(?:^|[\s"'\\/])ccstatusline\.ts(?=$|[\s"'])/.test(command);
65080
65094
  }
65081
65095
  function needsQuoting(filePath) {
65082
65096
  if (process.platform === "win32") {
@@ -65185,6 +65199,34 @@ function isBunxAvailable() {
65185
65199
  return false;
65186
65200
  }
65187
65201
  }
65202
+ function getClaudeCodeVersion() {
65203
+ try {
65204
+ const output = execSync6("claude --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"], timeout: 5000 }).trim();
65205
+ const match = /^(\d+\.\d+\.\d+)/.exec(output);
65206
+ return match?.[1] ?? null;
65207
+ } catch {
65208
+ return null;
65209
+ }
65210
+ }
65211
+ function isClaudeCodeVersionAtLeast(minVersion) {
65212
+ const version2 = getClaudeCodeVersion();
65213
+ if (!version2) {
65214
+ return false;
65215
+ }
65216
+ const current = version2.split(".").map(Number);
65217
+ const minimum = minVersion.split(".").map(Number);
65218
+ for (let i = 0;i < 3; i++) {
65219
+ const c = current[i] ?? 0;
65220
+ const m = minimum[i] ?? 0;
65221
+ if (c > m) {
65222
+ return true;
65223
+ }
65224
+ if (c < m) {
65225
+ return false;
65226
+ }
65227
+ }
65228
+ return true;
65229
+ }
65188
65230
  function buildCommand(baseCommand) {
65189
65231
  if (isCustomConfigPath()) {
65190
65232
  return `${baseCommand} --config ${quotePathIfNeeded(getConfigPath())}`;
@@ -65208,7 +65250,7 @@ async function loadSavedSettingsForHookSync() {
65208
65250
  return null;
65209
65251
  }
65210
65252
  }
65211
- async function installStatusLine(useBunx = false) {
65253
+ async function installStatusLine(useBunx = false, supportsRefreshInterval = false) {
65212
65254
  let settings;
65213
65255
  const backupPath = await backupClaudeSettings(".orig");
65214
65256
  try {
@@ -65219,11 +65261,15 @@ async function installStatusLine(useBunx = false) {
65219
65261
  settings = {};
65220
65262
  }
65221
65263
  const baseCommand = useBunx ? CCSTATUSLINE_COMMANDS.BUNX : CCSTATUSLINE_COMMANDS.NPM;
65264
+ const existingRefreshInterval = settings.statusLine?.refreshInterval;
65222
65265
  settings.statusLine = {
65223
65266
  type: "command",
65224
65267
  command: buildCommand(baseCommand),
65225
65268
  padding: 0
65226
65269
  };
65270
+ if (supportsRefreshInterval) {
65271
+ settings.statusLine.refreshInterval = existingRefreshInterval ?? 10;
65272
+ }
65227
65273
  await saveClaudeSettings(settings);
65228
65274
  const savedSettings = await loadSavedSettingsForHookSync();
65229
65275
  if (savedSettings) {
@@ -65256,6 +65302,31 @@ async function getExistingStatusLine() {
65256
65302
  return null;
65257
65303
  }
65258
65304
  }
65305
+ async function getRefreshInterval() {
65306
+ try {
65307
+ const settings = await loadClaudeSettings({ logErrors: false });
65308
+ return settings.statusLine?.refreshInterval ?? null;
65309
+ } catch {
65310
+ return null;
65311
+ }
65312
+ }
65313
+ async function setRefreshInterval(interval) {
65314
+ let settings;
65315
+ try {
65316
+ settings = await loadClaudeSettings({ logErrors: false });
65317
+ } catch {
65318
+ return;
65319
+ }
65320
+ if (!settings.statusLine) {
65321
+ return;
65322
+ }
65323
+ if (interval === null) {
65324
+ delete settings.statusLine.refreshInterval;
65325
+ } else {
65326
+ settings.statusLine.refreshInterval = interval;
65327
+ }
65328
+ await saveClaudeSettings(settings);
65329
+ }
65259
65330
  var readFile4, writeFile2, CCSTATUSLINE_COMMANDS;
65260
65331
  var init_claude_settings = __esm(() => {
65261
65332
  init_Settings();
@@ -65839,7 +65910,7 @@ var dist_default5 = Gradient;
65839
65910
 
65840
65911
  // src/tui/App.tsx
65841
65912
  init_claude_settings();
65842
- var import_react47 = __toESM(require_react(), 1);
65913
+ var import_react48 = __toESM(require_react(), 1);
65843
65914
 
65844
65915
  // src/utils/clone-settings.ts
65845
65916
  function cloneSettings(settings) {
@@ -66170,6 +66241,22 @@ async function installPowerlineFonts() {
66170
66241
  // src/tui/App.tsx
66171
66242
  init_terminal();
66172
66243
 
66244
+ // src/tui/claude-status.ts
66245
+ init_claude_settings();
66246
+ async function loadClaudeStatusLineState() {
66247
+ const [
66248
+ existingStatusLine,
66249
+ refreshInterval
66250
+ ] = await Promise.all([
66251
+ getExistingStatusLine(),
66252
+ getRefreshInterval()
66253
+ ]);
66254
+ return {
66255
+ existingStatusLine,
66256
+ refreshInterval
66257
+ };
66258
+ }
66259
+
66173
66260
  // src/tui/components/ColorMenu.tsx
66174
66261
  init_source();
66175
66262
  await init_build2();
@@ -69086,11 +69173,24 @@ var MainMenu = ({
69086
69173
  description: "Set global padding, separators, and color overrides that apply to all widgets"
69087
69174
  },
69088
69175
  "-",
69089
- {
69090
- label: isClaudeInstalled ? "\uD83D\uDD0C Uninstall from Claude Code" : "\uD83D\uDCE6 Install to Claude Code",
69091
- value: "install",
69092
- description: isClaudeInstalled ? "Remove ccstatusline from your Claude Code settings" : "Add ccstatusline to your Claude Code settings for automatic status line rendering"
69093
- }
69176
+ ...isClaudeInstalled ? [
69177
+ {
69178
+ label: "\uD83D\uDD27 Configure Status Line",
69179
+ value: "configureStatusLine",
69180
+ description: "Configure Claude Code status line settings like refresh interval"
69181
+ },
69182
+ {
69183
+ label: "\uD83D\uDD0C Uninstall from Claude Code",
69184
+ value: "install",
69185
+ description: "Remove ccstatusline from your Claude Code settings"
69186
+ }
69187
+ ] : [
69188
+ {
69189
+ label: "\uD83D\uDCE6 Install to Claude Code",
69190
+ value: "install",
69191
+ description: "Add ccstatusline to your Claude Code settings for automatic status line rendering"
69192
+ }
69193
+ ]
69094
69194
  ];
69095
69195
  if (hasChanges) {
69096
69196
  menuItems.push({
@@ -70207,10 +70307,148 @@ var PowerlineSetup = ({
70207
70307
  ]
70208
70308
  }, undefined, true, undefined, this);
70209
70309
  };
70310
+ // src/tui/components/RefreshIntervalMenu.tsx
70311
+ init_input_guards();
70312
+ await init_build2();
70313
+ var import_react44 = __toESM(require_react(), 1);
70314
+ var jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
70315
+ function getRefreshInputValue(interval) {
70316
+ return interval === null ? "" : String(interval);
70317
+ }
70318
+ function getRefreshIntervalSublabel(interval, supported) {
70319
+ if (!supported) {
70320
+ return "(requires Claude Code >=2.1.97)";
70321
+ }
70322
+ if (interval === null) {
70323
+ return "(not set)";
70324
+ }
70325
+ return `(${interval}s)`;
70326
+ }
70327
+ function buildConfigureStatusLineItems(refreshInterval, supportsRefreshInterval) {
70328
+ return [
70329
+ {
70330
+ label: "\uD83D\uDD04 Refresh Interval",
70331
+ sublabel: getRefreshIntervalSublabel(refreshInterval, supportsRefreshInterval),
70332
+ value: "refreshInterval",
70333
+ disabled: !supportsRefreshInterval,
70334
+ description: supportsRefreshInterval ? "How often Claude Code refreshes the status line by re-running the command. Enter value in seconds (1-60), or leave empty to remove." : "This setting requires Claude Code version 2.1.97 or later. Please update Claude Code to use this feature."
70335
+ }
70336
+ ];
70337
+ }
70338
+ function validateRefreshIntervalInput(value) {
70339
+ if (value === "") {
70340
+ return null;
70341
+ }
70342
+ const parsed = parseInt(value, 10);
70343
+ if (isNaN(parsed)) {
70344
+ return "Please enter a valid number";
70345
+ }
70346
+ if (parsed < 1) {
70347
+ return `Minimum interval is 1s (you entered ${parsed}s)`;
70348
+ }
70349
+ if (parsed > 60) {
70350
+ return `Maximum interval is 60s (you entered ${parsed}s)`;
70351
+ }
70352
+ return null;
70353
+ }
70354
+ var RefreshIntervalMenu = ({
70355
+ currentInterval,
70356
+ supportsRefreshInterval,
70357
+ onUpdate,
70358
+ onBack
70359
+ }) => {
70360
+ const [editingRefreshInterval, setEditingRefreshInterval] = import_react44.useState(false);
70361
+ const [refreshInput, setRefreshInput] = import_react44.useState(() => getRefreshInputValue(currentInterval));
70362
+ const [validationError, setValidationError] = import_react44.useState(null);
70363
+ use_input_default((input, key) => {
70364
+ if (editingRefreshInterval) {
70365
+ if (key.return) {
70366
+ if (refreshInput === "") {
70367
+ onUpdate(null);
70368
+ setEditingRefreshInterval(false);
70369
+ setValidationError(null);
70370
+ return;
70371
+ }
70372
+ const error48 = validateRefreshIntervalInput(refreshInput);
70373
+ if (error48) {
70374
+ setValidationError(error48);
70375
+ } else {
70376
+ const value = parseInt(refreshInput, 10);
70377
+ onUpdate(value);
70378
+ setEditingRefreshInterval(false);
70379
+ setValidationError(null);
70380
+ }
70381
+ } else if (key.escape) {
70382
+ setRefreshInput(getRefreshInputValue(currentInterval));
70383
+ setEditingRefreshInterval(false);
70384
+ setValidationError(null);
70385
+ } else if (key.backspace) {
70386
+ setRefreshInput(refreshInput.slice(0, -1));
70387
+ setValidationError(null);
70388
+ } else if (key.delete) {} else if (shouldInsertInput(input, key) && /\d/.test(input)) {
70389
+ const newValue = refreshInput + input;
70390
+ if (newValue.length <= 2) {
70391
+ setRefreshInput(newValue);
70392
+ setValidationError(null);
70393
+ }
70394
+ }
70395
+ return;
70396
+ }
70397
+ if (key.escape) {
70398
+ onBack();
70399
+ }
70400
+ });
70401
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
70402
+ flexDirection: "column",
70403
+ children: [
70404
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
70405
+ bold: true,
70406
+ children: "Configure Status Line"
70407
+ }, undefined, false, undefined, this),
70408
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
70409
+ color: "white",
70410
+ children: "Configure Claude Code status line settings"
70411
+ }, undefined, false, undefined, this),
70412
+ editingRefreshInterval ? /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
70413
+ marginTop: 1,
70414
+ flexDirection: "column",
70415
+ children: [
70416
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
70417
+ children: [
70418
+ "Enter refresh interval in seconds (1-60):",
70419
+ " ",
70420
+ refreshInput,
70421
+ refreshInput.length > 0 ? "s" : ""
70422
+ ]
70423
+ }, undefined, true, undefined, this),
70424
+ validationError ? /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
70425
+ color: "red",
70426
+ children: validationError
70427
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
70428
+ dimColor: true,
70429
+ children: "Press Enter to confirm, ESC to cancel. Leave empty to remove."
70430
+ }, undefined, false, undefined, this)
70431
+ ]
70432
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(List, {
70433
+ marginTop: 1,
70434
+ items: buildConfigureStatusLineItems(currentInterval, supportsRefreshInterval),
70435
+ onSelect: (value) => {
70436
+ if (value === "back") {
70437
+ onBack();
70438
+ return;
70439
+ }
70440
+ setRefreshInput(getRefreshInputValue(currentInterval));
70441
+ setEditingRefreshInterval(true);
70442
+ },
70443
+ showBackButton: true
70444
+ }, undefined, false, undefined, this)
70445
+ ]
70446
+ }, undefined, true, undefined, this);
70447
+ };
70210
70448
  // src/tui/components/StatusLinePreview.tsx
70211
70449
  init_source();
70212
70450
  await init_build2();
70213
- var import_react44 = __toESM(require_react(), 1);
70451
+ var import_react45 = __toESM(require_react(), 1);
70214
70452
 
70215
70453
  // src/utils/powerline-theme-index.ts
70216
70454
  function countPowerlineThemeSlots(entries) {
@@ -70244,7 +70482,7 @@ function advanceGlobalSeparatorIndex(currentIndex, widgets) {
70244
70482
  }
70245
70483
 
70246
70484
  // src/tui/components/StatusLinePreview.tsx
70247
- var jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
70485
+ var jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
70248
70486
  var renderSingleLine = (widgets, terminalWidth, settings, lineIndex, globalSeparatorIndex, globalPowerlineThemeIndex, preRenderedWidgets, preCalculatedMaxWidths) => {
70249
70487
  const context = {
70250
70488
  terminalWidth,
@@ -70257,7 +70495,7 @@ var renderSingleLine = (widgets, terminalWidth, settings, lineIndex, globalSepar
70257
70495
  return renderStatusLineWithInfo(widgets, settings, context, preRenderedWidgets, preCalculatedMaxWidths);
70258
70496
  };
70259
70497
  var StatusLinePreview = ({ lines, terminalWidth, settings, onTruncationChange }) => {
70260
- const { renderedLines, anyTruncated } = import_react44.default.useMemo(() => {
70498
+ const { renderedLines, anyTruncated } = import_react45.default.useMemo(() => {
70261
70499
  if (!settings)
70262
70500
  return { renderedLines: [], anyTruncated: false };
70263
70501
  const preRenderedLines = preRenderAllWidgets(lines, settings, { terminalWidth, isPreview: true, minimalist: settings.minimalistMode });
@@ -70283,29 +70521,29 @@ var StatusLinePreview = ({ lines, terminalWidth, settings, onTruncationChange })
70283
70521
  }
70284
70522
  return { renderedLines: result2, anyTruncated: truncated };
70285
70523
  }, [lines, terminalWidth, settings]);
70286
- import_react44.default.useEffect(() => {
70524
+ import_react45.default.useEffect(() => {
70287
70525
  onTruncationChange?.(anyTruncated);
70288
70526
  }, [anyTruncated, onTruncationChange]);
70289
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
70527
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
70290
70528
  flexDirection: "column",
70291
70529
  children: [
70292
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
70530
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
70293
70531
  borderStyle: "round",
70294
70532
  borderColor: "gray",
70295
70533
  borderDimColor: true,
70296
70534
  width: "100%",
70297
70535
  paddingLeft: 1,
70298
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
70536
+ children: /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
70299
70537
  children: [
70300
70538
  ">",
70301
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
70539
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
70302
70540
  dimColor: true,
70303
70541
  children: " Preview (ctrl+s to save configuration at any time)"
70304
70542
  }, undefined, false, undefined, this)
70305
70543
  ]
70306
70544
  }, undefined, true, undefined, this)
70307
70545
  }, undefined, false, undefined, this),
70308
- renderedLines.map((line, index) => /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
70546
+ renderedLines.map((line, index) => /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
70309
70547
  children: [
70310
70548
  " ",
70311
70549
  line,
@@ -70318,7 +70556,7 @@ var StatusLinePreview = ({ lines, terminalWidth, settings, onTruncationChange })
70318
70556
  // src/tui/components/TerminalOptionsMenu.tsx
70319
70557
  init_source();
70320
70558
  await init_build2();
70321
- var import_react45 = __toESM(require_react(), 1);
70559
+ var import_react46 = __toESM(require_react(), 1);
70322
70560
 
70323
70561
  // src/utils/color-sanitize.ts
70324
70562
  await init_widgets2();
@@ -70373,7 +70611,7 @@ function sanitizeLinesForColorLevel(lines, nextLevel) {
70373
70611
  }
70374
70612
 
70375
70613
  // src/tui/components/TerminalOptionsMenu.tsx
70376
- var jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
70614
+ var jsx_dev_runtime21 = __toESM(require_jsx_dev_runtime(), 1);
70377
70615
  function getNextColorLevel(level) {
70378
70616
  return (level + 1) % 4;
70379
70617
  }
@@ -70407,8 +70645,8 @@ var TerminalOptionsMenu = ({
70407
70645
  onUpdate,
70408
70646
  onBack
70409
70647
  }) => {
70410
- const [showColorWarning, setShowColorWarning] = import_react45.useState(false);
70411
- const [pendingColorLevel, setPendingColorLevel] = import_react45.useState(null);
70648
+ const [showColorWarning, setShowColorWarning] = import_react46.useState(false);
70649
+ const [pendingColorLevel, setPendingColorLevel] = import_react46.useState(null);
70412
70650
  const handleSelect = (value) => {
70413
70651
  if (value === "back") {
70414
70652
  onBack();
@@ -70456,27 +70694,27 @@ var TerminalOptionsMenu = ({
70456
70694
  onBack();
70457
70695
  }
70458
70696
  });
70459
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
70697
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
70460
70698
  flexDirection: "column",
70461
70699
  children: [
70462
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
70700
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70463
70701
  bold: true,
70464
70702
  children: "Terminal Options"
70465
70703
  }, undefined, false, undefined, this),
70466
- showColorWarning ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
70704
+ showColorWarning ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
70467
70705
  flexDirection: "column",
70468
70706
  marginTop: 1,
70469
70707
  children: [
70470
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
70708
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70471
70709
  color: "yellow",
70472
70710
  children: "⚠ Warning: Custom colors detected!"
70473
70711
  }, undefined, false, undefined, this),
70474
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
70712
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70475
70713
  children: "Switching color modes will reset custom ansi256 or hex colors to defaults."
70476
70714
  }, undefined, false, undefined, this),
70477
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
70715
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
70478
70716
  marginTop: 1,
70479
- children: /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ConfirmDialog, {
70717
+ children: /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ConfirmDialog, {
70480
70718
  message: "Continue?",
70481
70719
  onConfirm: handleColorConfirm,
70482
70720
  onCancel: handleColorCancel,
@@ -70484,13 +70722,13 @@ var TerminalOptionsMenu = ({
70484
70722
  }, undefined, false, undefined, this)
70485
70723
  }, undefined, false, undefined, this)
70486
70724
  ]
70487
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(jsx_dev_runtime20.Fragment, {
70725
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(jsx_dev_runtime21.Fragment, {
70488
70726
  children: [
70489
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
70727
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70490
70728
  color: "white",
70491
70729
  children: "Configure terminal-specific settings for optimal display"
70492
70730
  }, undefined, false, undefined, this),
70493
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(List, {
70731
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(List, {
70494
70732
  marginTop: 1,
70495
70733
  items: buildTerminalOptionsItems(settings.colorLevel),
70496
70734
  onSelect: handleSelect,
@@ -70519,8 +70757,8 @@ var getColorLevelLabel = (level) => {
70519
70757
  // src/tui/components/TerminalWidthMenu.tsx
70520
70758
  init_input_guards();
70521
70759
  await init_build2();
70522
- var import_react46 = __toESM(require_react(), 1);
70523
- var jsx_dev_runtime21 = __toESM(require_jsx_dev_runtime(), 1);
70760
+ var import_react47 = __toESM(require_react(), 1);
70761
+ var jsx_dev_runtime22 = __toESM(require_jsx_dev_runtime(), 1);
70524
70762
  var TERMINAL_WIDTH_OPTIONS = ["full", "full-minus-40", "full-until-compact"];
70525
70763
  function getTerminalWidthSelectionIndex(selectedOption) {
70526
70764
  const selectedIndex = TERMINAL_WIDTH_OPTIONS.indexOf(selectedOption);
@@ -70567,11 +70805,11 @@ var TerminalWidthMenu = ({
70567
70805
  onUpdate,
70568
70806
  onBack
70569
70807
  }) => {
70570
- const [selectedOption, setSelectedOption] = import_react46.useState(settings.flexMode);
70571
- const [compactThreshold, setCompactThreshold] = import_react46.useState(settings.compactThreshold);
70572
- const [editingThreshold, setEditingThreshold] = import_react46.useState(false);
70573
- const [thresholdInput, setThresholdInput] = import_react46.useState(String(settings.compactThreshold));
70574
- const [validationError, setValidationError] = import_react46.useState(null);
70808
+ const [selectedOption, setSelectedOption] = import_react47.useState(settings.flexMode);
70809
+ const [compactThreshold, setCompactThreshold] = import_react47.useState(settings.compactThreshold);
70810
+ const [editingThreshold, setEditingThreshold] = import_react47.useState(false);
70811
+ const [thresholdInput, setThresholdInput] = import_react47.useState(String(settings.compactThreshold));
70812
+ const [validationError, setValidationError] = import_react47.useState(null);
70575
70813
  use_input_default((input, key) => {
70576
70814
  if (editingThreshold) {
70577
70815
  if (key.return) {
@@ -70610,27 +70848,27 @@ var TerminalWidthMenu = ({
70610
70848
  onBack();
70611
70849
  }
70612
70850
  });
70613
- return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
70851
+ return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
70614
70852
  flexDirection: "column",
70615
70853
  children: [
70616
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70854
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
70617
70855
  bold: true,
70618
70856
  children: "Terminal Width"
70619
70857
  }, undefined, false, undefined, this),
70620
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70858
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
70621
70859
  color: "white",
70622
70860
  children: "These settings affect where long lines are truncated, and where right-alignment occurs when using flex separators"
70623
70861
  }, undefined, false, undefined, this),
70624
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70862
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
70625
70863
  dimColor: true,
70626
70864
  wrap: "wrap",
70627
70865
  children: "Claude code does not currently provide an available width variable for the statusline and features like IDE integration, auto-compaction notices, etc all cause the statusline to wrap if we do not truncate it"
70628
70866
  }, undefined, false, undefined, this),
70629
- editingThreshold ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
70867
+ editingThreshold ? /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
70630
70868
  marginTop: 1,
70631
70869
  flexDirection: "column",
70632
70870
  children: [
70633
- /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70871
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
70634
70872
  children: [
70635
70873
  "Enter compact threshold (1-99):",
70636
70874
  " ",
@@ -70638,15 +70876,15 @@ var TerminalWidthMenu = ({
70638
70876
  "%"
70639
70877
  ]
70640
70878
  }, undefined, true, undefined, this),
70641
- validationError ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70879
+ validationError ? /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
70642
70880
  color: "red",
70643
70881
  children: validationError
70644
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Text, {
70882
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
70645
70883
  dimColor: true,
70646
70884
  children: "Press Enter to confirm, ESC to cancel"
70647
70885
  }, undefined, false, undefined, this)
70648
70886
  ]
70649
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(List, {
70887
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(List, {
70650
70888
  marginTop: 1,
70651
70889
  items: buildTerminalWidthItems(selectedOption, compactThreshold),
70652
70890
  initialSelection: getTerminalWidthSelectionIndex(selectedOption),
@@ -70672,7 +70910,7 @@ var TerminalWidthMenu = ({
70672
70910
  }, undefined, true, undefined, this);
70673
70911
  };
70674
70912
  // src/tui/App.tsx
70675
- var jsx_dev_runtime22 = __toESM(require_jsx_dev_runtime(), 1);
70913
+ var jsx_dev_runtime23 = __toESM(require_jsx_dev_runtime(), 1);
70676
70914
  var GITHUB_REPO_URL = "https://github.com/sirmalloc/ccstatusline";
70677
70915
  function getConfirmCancelScreen(confirmDialog) {
70678
70916
  return confirmDialog?.cancelScreen ?? "main";
@@ -70687,23 +70925,28 @@ function clearInstallMenuSelection(menuSelections) {
70687
70925
  }
70688
70926
  var App2 = () => {
70689
70927
  const { exit } = use_app_default();
70690
- const [settings, setSettings] = import_react47.useState(null);
70691
- const [originalSettings, setOriginalSettings] = import_react47.useState(null);
70692
- const [hasChanges, setHasChanges] = import_react47.useState(false);
70693
- const [screen, setScreen] = import_react47.useState("main");
70694
- const [selectedLine, setSelectedLine] = import_react47.useState(0);
70695
- const [menuSelections, setMenuSelections] = import_react47.useState({});
70696
- const [confirmDialog, setConfirmDialog] = import_react47.useState(null);
70697
- const [isClaudeInstalled, setIsClaudeInstalled] = import_react47.useState(false);
70698
- const [terminalWidth, setTerminalWidth] = import_react47.useState(process.stdout.columns || 80);
70699
- const [powerlineFontStatus, setPowerlineFontStatus] = import_react47.useState({ installed: false });
70700
- const [installingFonts, setInstallingFonts] = import_react47.useState(false);
70701
- const [fontInstallMessage, setFontInstallMessage] = import_react47.useState(null);
70702
- const [existingStatusLine, setExistingStatusLine] = import_react47.useState(null);
70703
- const [flashMessage, setFlashMessage] = import_react47.useState(null);
70704
- const [previewIsTruncated, setPreviewIsTruncated] = import_react47.useState(false);
70705
- import_react47.useEffect(() => {
70706
- getExistingStatusLine().then(setExistingStatusLine);
70928
+ const [settings, setSettings] = import_react48.useState(null);
70929
+ const [originalSettings, setOriginalSettings] = import_react48.useState(null);
70930
+ const [hasChanges, setHasChanges] = import_react48.useState(false);
70931
+ const [screen, setScreen] = import_react48.useState("main");
70932
+ const [selectedLine, setSelectedLine] = import_react48.useState(0);
70933
+ const [menuSelections, setMenuSelections] = import_react48.useState({});
70934
+ const [confirmDialog, setConfirmDialog] = import_react48.useState(null);
70935
+ const [isClaudeInstalled, setIsClaudeInstalled] = import_react48.useState(false);
70936
+ const [terminalWidth, setTerminalWidth] = import_react48.useState(process.stdout.columns || 80);
70937
+ const [powerlineFontStatus, setPowerlineFontStatus] = import_react48.useState({ installed: false });
70938
+ const [installingFonts, setInstallingFonts] = import_react48.useState(false);
70939
+ const [fontInstallMessage, setFontInstallMessage] = import_react48.useState(null);
70940
+ const [existingStatusLine, setExistingStatusLine] = import_react48.useState(null);
70941
+ const [flashMessage, setFlashMessage] = import_react48.useState(null);
70942
+ const [previewIsTruncated, setPreviewIsTruncated] = import_react48.useState(false);
70943
+ const [currentRefreshInterval, setCurrentRefreshInterval] = import_react48.useState(null);
70944
+ const [supportsRefreshInterval] = import_react48.useState(() => isClaudeCodeVersionAtLeast("2.1.97"));
70945
+ import_react48.useEffect(() => {
70946
+ loadClaudeStatusLineState().then((statusLineState) => {
70947
+ setExistingStatusLine(statusLineState.existingStatusLine);
70948
+ setCurrentRefreshInterval(statusLineState.refreshInterval);
70949
+ });
70707
70950
  loadSettings().then((loadedSettings) => {
70708
70951
  source_default.level = loadedSettings.colorLevel;
70709
70952
  setSettings(loadedSettings);
@@ -70723,13 +70966,13 @@ var App2 = () => {
70723
70966
  process.stdout.off("resize", handleResize);
70724
70967
  };
70725
70968
  }, []);
70726
- import_react47.useEffect(() => {
70969
+ import_react48.useEffect(() => {
70727
70970
  if (originalSettings) {
70728
70971
  const hasAnyChanges = JSON.stringify(settings) !== JSON.stringify(originalSettings);
70729
70972
  setHasChanges(hasAnyChanges);
70730
70973
  }
70731
70974
  }, [settings, originalSettings]);
70732
- import_react47.useEffect(() => {
70975
+ import_react48.useEffect(() => {
70733
70976
  if (flashMessage) {
70734
70977
  const timer = setTimeout(() => {
70735
70978
  setFlashMessage(null);
@@ -70755,7 +70998,7 @@ var App2 = () => {
70755
70998
  })();
70756
70999
  }
70757
71000
  });
70758
- const handleInstallSelection = import_react47.useCallback((command, displayName, useBunx) => {
71001
+ const handleInstallSelection = import_react48.useCallback((command, displayName, useBunx) => {
70759
71002
  getExistingStatusLine().then((existing) => {
70760
71003
  const isAlreadyInstalled = isKnownCommand(existing ?? "");
70761
71004
  let message;
@@ -70775,30 +71018,32 @@ Continue?`;
70775
71018
  message,
70776
71019
  cancelScreen: "install",
70777
71020
  action: async () => {
70778
- await installStatusLine(useBunx);
71021
+ await installStatusLine(useBunx, supportsRefreshInterval);
71022
+ const installedStatusLineState = await loadClaudeStatusLineState();
70779
71023
  setIsClaudeInstalled(true);
70780
- setExistingStatusLine(command);
71024
+ setExistingStatusLine(installedStatusLineState.existingStatusLine ?? command);
71025
+ setCurrentRefreshInterval(installedStatusLineState.refreshInterval);
70781
71026
  setScreen("main");
70782
71027
  setConfirmDialog(null);
70783
71028
  }
70784
71029
  });
70785
71030
  setScreen("confirm");
70786
71031
  });
70787
- }, []);
70788
- const handleNpxInstall = import_react47.useCallback(() => {
71032
+ }, [supportsRefreshInterval]);
71033
+ const handleNpxInstall = import_react48.useCallback(() => {
70789
71034
  setMenuSelections((prev) => ({ ...prev, install: 0 }));
70790
71035
  handleInstallSelection(CCSTATUSLINE_COMMANDS.NPM, "npx", false);
70791
71036
  }, [handleInstallSelection]);
70792
- const handleBunxInstall = import_react47.useCallback(() => {
71037
+ const handleBunxInstall = import_react48.useCallback(() => {
70793
71038
  setMenuSelections((prev) => ({ ...prev, install: 1 }));
70794
71039
  handleInstallSelection(CCSTATUSLINE_COMMANDS.BUNX, "bunx", true);
70795
71040
  }, [handleInstallSelection]);
70796
- const handleInstallMenuCancel = import_react47.useCallback(() => {
71041
+ const handleInstallMenuCancel = import_react48.useCallback(() => {
70797
71042
  setMenuSelections(clearInstallMenuSelection);
70798
71043
  setScreen("main");
70799
71044
  }, []);
70800
71045
  if (!settings) {
70801
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
71046
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
70802
71047
  children: "Loading settings..."
70803
71048
  }, undefined, false, undefined, this);
70804
71049
  }
@@ -70810,6 +71055,7 @@ Continue?`;
70810
71055
  await uninstallStatusLine();
70811
71056
  setIsClaudeInstalled(false);
70812
71057
  setExistingStatusLine(null);
71058
+ setCurrentRefreshInterval(null);
70813
71059
  setScreen("main");
70814
71060
  setConfirmDialog(null);
70815
71061
  }
@@ -70839,6 +71085,9 @@ Continue?`;
70839
71085
  case "install":
70840
71086
  handleInstallUninstall();
70841
71087
  break;
71088
+ case "configureStatusLine":
71089
+ setScreen("refreshInterval");
71090
+ break;
70842
71091
  case "starGithub":
70843
71092
  setConfirmDialog({
70844
71093
  message: `Open the ccstatusline GitHub repository in your browser?
@@ -70887,44 +71136,44 @@ ${GITHUB_REPO_URL}`,
70887
71136
  setSelectedLine(lineIndex);
70888
71137
  setScreen("items");
70889
71138
  };
70890
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
71139
+ return /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
70891
71140
  flexDirection: "column",
70892
71141
  children: [
70893
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
71142
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
70894
71143
  marginBottom: 1,
70895
71144
  children: [
70896
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
71145
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
70897
71146
  bold: true,
70898
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(dist_default5, {
71147
+ children: /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(dist_default5, {
70899
71148
  name: "retro",
70900
71149
  children: "CCStatusline Configuration"
70901
71150
  }, undefined, false, undefined, this)
70902
71151
  }, undefined, false, undefined, this),
70903
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
71152
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
70904
71153
  bold: true,
70905
71154
  children: ` | ${getPackageVersion() && `v${getPackageVersion()}`}`
70906
71155
  }, undefined, false, undefined, this),
70907
- flashMessage && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
71156
+ flashMessage && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
70908
71157
  color: flashMessage.color,
70909
71158
  bold: true,
70910
71159
  children: ` ${flashMessage.text}`
70911
71160
  }, undefined, false, undefined, this)
70912
71161
  ]
70913
71162
  }, undefined, true, undefined, this),
70914
- isCustomConfigPath() && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
71163
+ isCustomConfigPath() && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text, {
70915
71164
  dimColor: true,
70916
71165
  children: `Config: ${getConfigPath()}`
70917
71166
  }, undefined, false, undefined, this),
70918
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(StatusLinePreview, {
71167
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(StatusLinePreview, {
70919
71168
  lines: settings.lines,
70920
71169
  terminalWidth,
70921
71170
  settings,
70922
71171
  onTruncationChange: setPreviewIsTruncated
70923
71172
  }, undefined, false, undefined, this),
70924
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
71173
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
70925
71174
  marginTop: 1,
70926
71175
  children: [
70927
- screen === "main" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(MainMenu, {
71176
+ screen === "main" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(MainMenu, {
70928
71177
  onSelect: (value, index) => {
70929
71178
  if (value !== "save" && value !== "exit") {
70930
71179
  setMenuSelections((prev) => ({ ...prev, main: index }));
@@ -70938,7 +71187,7 @@ ${GITHUB_REPO_URL}`,
70938
71187
  settings,
70939
71188
  previewIsTruncated
70940
71189
  }, undefined, false, undefined, this),
70941
- screen === "lines" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(LineSelector, {
71190
+ screen === "lines" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(LineSelector, {
70942
71191
  lines: settings.lines,
70943
71192
  onSelect: (line) => {
70944
71193
  setMenuSelections((prev) => ({ ...prev, lines: line }));
@@ -70953,7 +71202,7 @@ ${GITHUB_REPO_URL}`,
70953
71202
  title: "Select Line to Edit Items",
70954
71203
  allowEditing: true
70955
71204
  }, undefined, false, undefined, this),
70956
- screen === "items" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ItemsEditor, {
71205
+ screen === "items" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(ItemsEditor, {
70957
71206
  widgets: settings.lines[selectedLine] ?? [],
70958
71207
  onUpdate: (widgets) => {
70959
71208
  updateLine(selectedLine, widgets);
@@ -70965,7 +71214,7 @@ ${GITHUB_REPO_URL}`,
70965
71214
  lineNumber: selectedLine + 1,
70966
71215
  settings
70967
71216
  }, undefined, false, undefined, this),
70968
- screen === "colorLines" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(LineSelector, {
71217
+ screen === "colorLines" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(LineSelector, {
70969
71218
  lines: settings.lines,
70970
71219
  onLinesUpdate: updateLines,
70971
71220
  onSelect: (line) => {
@@ -70983,7 +71232,7 @@ ${GITHUB_REPO_URL}`,
70983
71232
  settings,
70984
71233
  allowEditing: false
70985
71234
  }, undefined, false, undefined, this),
70986
- screen === "colors" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ColorMenu, {
71235
+ screen === "colors" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(ColorMenu, {
70987
71236
  widgets: settings.lines[selectedLine] ?? [],
70988
71237
  lineIndex: selectedLine,
70989
71238
  settings,
@@ -70996,7 +71245,7 @@ ${GITHUB_REPO_URL}`,
70996
71245
  setScreen("colorLines");
70997
71246
  }
70998
71247
  }, undefined, false, undefined, this),
70999
- screen === "terminalConfig" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TerminalOptionsMenu, {
71248
+ screen === "terminalConfig" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TerminalOptionsMenu, {
71000
71249
  settings,
71001
71250
  onUpdate: (updatedSettings) => {
71002
71251
  setSettings(updatedSettings);
@@ -71010,7 +71259,7 @@ ${GITHUB_REPO_URL}`,
71010
71259
  }
71011
71260
  }
71012
71261
  }, undefined, false, undefined, this),
71013
- screen === "terminalWidth" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TerminalWidthMenu, {
71262
+ screen === "terminalWidth" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(TerminalWidthMenu, {
71014
71263
  settings,
71015
71264
  onUpdate: (updatedSettings) => {
71016
71265
  setSettings(updatedSettings);
@@ -71019,7 +71268,7 @@ ${GITHUB_REPO_URL}`,
71019
71268
  setScreen("terminalConfig");
71020
71269
  }
71021
71270
  }, undefined, false, undefined, this),
71022
- screen === "globalOverrides" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(GlobalOverridesMenu, {
71271
+ screen === "globalOverrides" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(GlobalOverridesMenu, {
71023
71272
  settings,
71024
71273
  onUpdate: (updatedSettings) => {
71025
71274
  setSettings(updatedSettings);
@@ -71029,7 +71278,7 @@ ${GITHUB_REPO_URL}`,
71029
71278
  setScreen("main");
71030
71279
  }
71031
71280
  }, undefined, false, undefined, this),
71032
- screen === "confirm" && confirmDialog && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ConfirmDialog, {
71281
+ screen === "confirm" && confirmDialog && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(ConfirmDialog, {
71033
71282
  message: confirmDialog.message,
71034
71283
  onConfirm: () => void confirmDialog.action(),
71035
71284
  onCancel: () => {
@@ -71037,7 +71286,7 @@ ${GITHUB_REPO_URL}`,
71037
71286
  setConfirmDialog(null);
71038
71287
  }
71039
71288
  }, undefined, false, undefined, this),
71040
- screen === "install" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(InstallMenu, {
71289
+ screen === "install" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(InstallMenu, {
71041
71290
  bunxAvailable: isBunxAvailable(),
71042
71291
  existingStatusLine,
71043
71292
  onSelectNpx: handleNpxInstall,
@@ -71045,7 +71294,31 @@ ${GITHUB_REPO_URL}`,
71045
71294
  onCancel: handleInstallMenuCancel,
71046
71295
  initialSelection: menuSelections.install
71047
71296
  }, undefined, false, undefined, this),
71048
- screen === "powerline" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(PowerlineSetup, {
71297
+ screen === "refreshInterval" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(RefreshIntervalMenu, {
71298
+ currentInterval: currentRefreshInterval,
71299
+ supportsRefreshInterval,
71300
+ onUpdate: (interval) => {
71301
+ const previous = currentRefreshInterval;
71302
+ setCurrentRefreshInterval(interval);
71303
+ setRefreshInterval(interval).then(() => {
71304
+ setFlashMessage({
71305
+ text: "✓ Refresh interval updated",
71306
+ color: "green"
71307
+ });
71308
+ }).catch(() => {
71309
+ setCurrentRefreshInterval(previous);
71310
+ setFlashMessage({
71311
+ text: "✗ Failed to save refresh interval",
71312
+ color: "red"
71313
+ });
71314
+ });
71315
+ setScreen("main");
71316
+ },
71317
+ onBack: () => {
71318
+ setScreen("main");
71319
+ }
71320
+ }, undefined, false, undefined, this),
71321
+ screen === "powerline" && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(PowerlineSetup, {
71049
71322
  settings,
71050
71323
  powerlineFontStatus,
71051
71324
  onUpdate: (updatedSettings) => {
@@ -71079,7 +71352,7 @@ ${GITHUB_REPO_URL}`,
71079
71352
  };
71080
71353
  function runTUI() {
71081
71354
  process.stdout.write("\x1B[2J\x1B[H");
71082
- render_default(/* @__PURE__ */ jsx_dev_runtime22.jsxDEV(App2, {}, undefined, false, undefined, this));
71355
+ render_default(/* @__PURE__ */ jsx_dev_runtime23.jsxDEV(App2, {}, undefined, false, undefined, this));
71083
71356
  }
71084
71357
  // src/types/StatusJSON.ts
71085
71358
  init_zod();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline-usage",
3
- "version": "2.3.14",
3
+ "version": "2.3.16",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",
@@ -35,7 +35,7 @@
35
35
  "eslint-plugin-react": "^7.37.5",
36
36
  "eslint-plugin-react-hooks": "^7.0.1",
37
37
  "globals": "^17.3.0",
38
- "https-proxy-agent": "^7.0.0",
38
+ "https-proxy-agent": "^8.0.0",
39
39
  "ink": "6.2.0",
40
40
  "ink-gradient": "^4.0.0",
41
41
  "ink-select-input": "^6.2.0",