ccstatusline 2.1.5 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,14 +46,16 @@
46
46
 
47
47
  ## 🆕 Recent Updates
48
48
 
49
- ### v2.1.0 - v2.1.4 - Usage widgets, links, new git insertions / deletions widgets, and reliability fixes
49
+ ### v2.1.0 - v2.1.7 - Usage widgets, links, new git insertions / deletions widgets, and reliability fixes
50
50
 
51
- - **🧩 New Usage widgets (v2.1.0)** - Added **Session Usage**, **Weekly Usage**, **Reset Timer**, and **Context Bar** widgets.
51
+ - **🧩 New Usage widgets (v2.1.0)** - Added **Session Usage**, **Weekly Usage**, **Block Reset Timer**, and **Context Bar** widgets.
52
52
  - **📊 More accurate counts (v2.1.0)** - Usage/context widgets now use new statusline JSON metrics when available for more accurate token and context counts.
53
53
  - **🪟 Windows empty file bug fix (v2.1.1)** - Fixed a Windows issue that could create an empty `c:\dev\null` file.
54
54
  - **🔗 New Link widget (v2.1.3)** - Added a new **Link** widget with clickable OSC8 rendering, preview parity, and raw mode support.
55
55
  - **➕ New Git Insertions widget (v2.1.4)** - Added a dedicated Git widget that shows only uncommitted insertions (e.g., `+42`).
56
56
  - **➖ New Git Deletions widget (v2.1.4)** - Added a dedicated Git widget that shows only uncommitted deletions (e.g., `-10`).
57
+ - **🧠 Context format fallback fix (v2.1.6)** - When `context_window_size` is missing, context widgets now infer 1M models from long-context labels such as `[1m]` and `1M context` in model identifiers.
58
+ - **⏳ Weekly reset timer split (v2.1.7)** - Added a separate `Weekly Reset Timer` widget.
57
59
 
58
60
  ### v2.0.26 - v2.0.29 - Performance, git internals, and workflow improvements
59
61
 
@@ -425,7 +427,8 @@ bun run example
425
427
  - **Session Name** - Shows the session name set via `/rename` command in Claude Code
426
428
  - **Claude Session ID** - Shows the current Claude Code session ID from status JSON
427
429
  - **Block Timer** - Shows time elapsed in current 5-hour block or progress bar
428
- - **Reset Timer** - Shows time remaining until the current 5-hour block resets
430
+ - **Block Reset Timer** - Shows time remaining until the current 5-hour block resets
431
+ - **Weekly Reset Timer** - Shows time remaining until the weekly usage window resets
429
432
  - **Current Working Directory** - Shows current working directory with segment limit, fish-style abbreviation, and optional `~` home abbreviation
430
433
  - **Version** - Shows Claude Code version
431
434
  - **Output Style** - Shows the currently set output style in Claude Code
@@ -434,8 +437,8 @@ bun run example
434
437
  - **Tokens Cached** - Shows cached tokens used
435
438
  - **Tokens Total** - Shows total tokens used
436
439
  - **Context Length** - Shows current context length in tokens
437
- - **Context Percentage** - Shows percentage of context limit used (dynamic: 1M for model IDs with `[1m]` suffix, 200k otherwise)
438
- - **Context Percentage (usable)** - Shows percentage of usable context (dynamic: 800k for model IDs with `[1m]` suffix, 160k otherwise, accounting for auto-compact at 80%)
440
+ - **Context Percentage** - Shows percentage of context limit used (dynamic: 1M for model IDs with long-context labels like `[1m]` or `1M context`, 200k otherwise)
441
+ - **Context Percentage (usable)** - Shows percentage of usable context (dynamic: 800k for model IDs with long-context labels like `[1m]` or `1M context`, 160k otherwise, accounting for auto-compact at 80%)
439
442
  - **Context Bar** - Shows context usage as a progress bar with short/full display modes
440
443
  - **Terminal Width** - Shows detected terminal width (for debugging)
441
444
  - **Memory Usage** - Shows system memory usage (used/total, e.g., "Mem: 12.4G/16.0G")
@@ -527,6 +530,8 @@ Widget-specific shortcuts:
527
530
  - **Git widgets**: `h` toggle hide `no git` output
528
531
  - **Context % widgets**: `u` toggle used vs remaining display
529
532
  - **Block Timer**: `p` cycle display mode (time/full bar/short bar)
533
+ - **Block Reset Timer**: `p` cycle display mode (time/full bar/short bar)
534
+ - **Weekly Reset Timer**: `p` cycle display mode (time/full bar/short bar)
530
535
  - **Current Working Dir**: `h` home abbreviation, `s` segment editor, `f` fish-style path
531
536
  - **Custom Command**: `e` command, `w` max width, `t` timeout, `p` preserve ANSI colors
532
537
  - **Link**: `u` URL, `e` link text
@@ -51478,7 +51478,7 @@ import { execSync as execSync3 } from "child_process";
51478
51478
  import * as fs5 from "fs";
51479
51479
  import * as path4 from "path";
51480
51480
  var __dirname = "/Users/sirmalloc/Projects/Personal/ccstatusline/src/utils";
51481
- var PACKAGE_VERSION = "2.1.5";
51481
+ var PACKAGE_VERSION = "2.1.7";
51482
51482
  function getPackageVersion() {
51483
51483
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51484
51484
  return PACKAGE_VERSION;
@@ -53032,30 +53032,77 @@ function truncateStyledText(text, maxWidth, options = {}) {
53032
53032
  }
53033
53033
 
53034
53034
  // src/utils/model-context.ts
53035
+ var DEFAULT_CONTEXT_WINDOW_SIZE = 200000;
53036
+ var USABLE_CONTEXT_RATIO = 0.8;
53035
53037
  function toValidWindowSize(value) {
53036
53038
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
53037
53039
  return null;
53038
53040
  }
53039
53041
  return value;
53040
53042
  }
53041
- function getContextConfig(modelId, contextWindowSize) {
53043
+ function parseContextWindowSize(modelIdentifier) {
53044
+ const delimitedMatch = /(?:\(|\[)\s*(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])\s*(?:\)|\])/i.exec(modelIdentifier);
53045
+ if (delimitedMatch) {
53046
+ const delimitedValue = delimitedMatch[1];
53047
+ const delimitedUnit = delimitedMatch[2];
53048
+ if (!delimitedValue || !delimitedUnit) {
53049
+ return null;
53050
+ }
53051
+ const parsed2 = Number.parseFloat(delimitedValue.replace(/[,_]/g, ""));
53052
+ if (Number.isFinite(parsed2) && parsed2 > 0) {
53053
+ return Math.round(parsed2 * (delimitedUnit.toLowerCase() === "m" ? 1e6 : 1000));
53054
+ }
53055
+ }
53056
+ const contextMatch = /\b(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])(?:\s*(?:token\s*)?context)?\b/i.exec(modelIdentifier);
53057
+ if (!contextMatch) {
53058
+ return null;
53059
+ }
53060
+ const contextValue = contextMatch[1];
53061
+ const contextUnit = contextMatch[2];
53062
+ if (!contextValue || !contextUnit) {
53063
+ return null;
53064
+ }
53065
+ const parsed = Number.parseFloat(contextValue.replace(/[,_]/g, ""));
53066
+ if (!Number.isFinite(parsed) || parsed <= 0) {
53067
+ return null;
53068
+ }
53069
+ return Math.round(parsed * (contextUnit.toLowerCase() === "m" ? 1e6 : 1000));
53070
+ }
53071
+ function getModelContextIdentifier(model) {
53072
+ if (typeof model === "string") {
53073
+ const trimmed = model.trim();
53074
+ return trimmed.length > 0 ? trimmed : undefined;
53075
+ }
53076
+ if (!model) {
53077
+ return;
53078
+ }
53079
+ const id = model.id?.trim();
53080
+ const displayName = model.display_name?.trim();
53081
+ if (id && displayName) {
53082
+ return `${id} ${displayName}`;
53083
+ }
53084
+ return id ?? displayName;
53085
+ }
53086
+ function getContextConfig(modelIdentifier, contextWindowSize) {
53042
53087
  const statusWindowSize = toValidWindowSize(contextWindowSize);
53043
53088
  if (statusWindowSize !== null) {
53044
53089
  return {
53045
53090
  maxTokens: statusWindowSize,
53046
- usableTokens: Math.floor(statusWindowSize * 0.8)
53091
+ usableTokens: Math.floor(statusWindowSize * USABLE_CONTEXT_RATIO)
53047
53092
  };
53048
53093
  }
53049
53094
  const defaultConfig = {
53050
- maxTokens: 200000,
53051
- usableTokens: 160000
53095
+ maxTokens: DEFAULT_CONTEXT_WINDOW_SIZE,
53096
+ usableTokens: Math.floor(DEFAULT_CONTEXT_WINDOW_SIZE * USABLE_CONTEXT_RATIO)
53052
53097
  };
53053
- if (!modelId)
53098
+ if (!modelIdentifier) {
53054
53099
  return defaultConfig;
53055
- if (modelId.toLowerCase().includes("[1m]")) {
53100
+ }
53101
+ const inferredWindowSize = parseContextWindowSize(modelIdentifier);
53102
+ if (inferredWindowSize !== null) {
53056
53103
  return {
53057
- maxTokens: 1e6,
53058
- usableTokens: 800000
53104
+ maxTokens: inferredWindowSize,
53105
+ usableTokens: Math.floor(inferredWindowSize * USABLE_CONTEXT_RATIO)
53059
53106
  };
53060
53107
  }
53061
53108
  return defaultConfig;
@@ -53070,9 +53117,8 @@ function calculateContextPercentage(context) {
53070
53117
  if (!context.tokenMetrics) {
53071
53118
  return 0;
53072
53119
  }
53073
- const model = context.data?.model;
53074
- const modelId = typeof model === "string" ? model : model?.id;
53075
- const contextConfig = getContextConfig(modelId, contextWindowMetrics.windowSize);
53120
+ const modelIdentifier = getModelContextIdentifier(context.data?.model);
53121
+ const contextConfig = getContextConfig(modelIdentifier, contextWindowMetrics.windowSize);
53076
53122
  return Math.min(100, context.tokenMetrics.contextLength / contextConfig.maxTokens * 100);
53077
53123
  }
53078
53124
 
@@ -53880,9 +53926,8 @@ class ContextPercentageWidget {
53880
53926
  return formatRawOrLabeledValue(item, "Ctx: ", `${displayPercentage.toFixed(1)}%`);
53881
53927
  }
53882
53928
  if (context.tokenMetrics) {
53883
- const model = context.data?.model;
53884
- const modelId = typeof model === "string" ? model : model?.id;
53885
- const contextConfig = getContextConfig(modelId, contextWindowMetrics.windowSize);
53929
+ const modelIdentifier = getModelContextIdentifier(context.data?.model);
53930
+ const contextConfig = getContextConfig(modelIdentifier, contextWindowMetrics.windowSize);
53886
53931
  const usedPercentage = Math.min(100, context.tokenMetrics.contextLength / contextConfig.maxTokens * 100);
53887
53932
  const displayPercentage = isInverse ? 100 - usedPercentage : usedPercentage;
53888
53933
  return formatRawOrLabeledValue(item, "Ctx: ", `${displayPercentage.toFixed(1)}%`);
@@ -53926,10 +53971,9 @@ class ContextPercentageUsableWidget {
53926
53971
  }
53927
53972
  render(item, context, settings) {
53928
53973
  const isInverse = isContextInverse(item);
53929
- const model = context.data?.model;
53930
- const modelId = typeof model === "string" ? model : model?.id;
53974
+ const modelIdentifier = getModelContextIdentifier(context.data?.model);
53931
53975
  const contextWindowMetrics = getContextWindowMetrics(context.data);
53932
- const contextConfig = getContextConfig(modelId, contextWindowMetrics.windowSize);
53976
+ const contextConfig = getContextConfig(modelIdentifier, contextWindowMetrics.windowSize);
53933
53977
  if (context.isPreview) {
53934
53978
  const previewValue = isInverse ? "88.4%" : "11.6%";
53935
53979
  return formatRawOrLabeledValue(item, "Ctx(u): ", previewValue);
@@ -55646,12 +55690,14 @@ var CACHE_MAX_AGE = 180;
55646
55690
  var LOCK_MAX_AGE = 30;
55647
55691
  var TOKEN_CACHE_MAX_AGE = 3600;
55648
55692
  var FIVE_HOUR_BLOCK_MS = 5 * 60 * 60 * 1000;
55693
+ var SEVEN_DAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
55649
55694
  var UsageErrorSchema = exports_external.enum(["no-credentials", "timeout", "api-error", "parse-error"]);
55650
55695
  var UsageCredentialsSchema = exports_external.object({ claudeAiOauth: exports_external.object({ accessToken: exports_external.string().nullable().optional() }).optional() });
55651
55696
  var CachedUsageDataSchema = exports_external.object({
55652
55697
  sessionUsage: exports_external.number().nullable().optional(),
55653
55698
  sessionResetAt: exports_external.string().nullable().optional(),
55654
55699
  weeklyUsage: exports_external.number().nullable().optional(),
55700
+ weeklyResetAt: exports_external.string().nullable().optional(),
55655
55701
  extraUsageEnabled: exports_external.boolean().nullable().optional(),
55656
55702
  extraUsageLimit: exports_external.number().nullable().optional(),
55657
55703
  extraUsageUsed: exports_external.number().nullable().optional(),
@@ -55661,10 +55707,12 @@ var CachedUsageDataSchema = exports_external.object({
55661
55707
  var UsageApiResponseSchema = exports_external.object({
55662
55708
  five_hour: exports_external.object({
55663
55709
  utilization: exports_external.number().nullable().optional(),
55664
- reset_at: exports_external.string().nullable().optional(),
55665
55710
  resets_at: exports_external.string().nullable().optional()
55666
55711
  }).optional(),
55667
- seven_day: exports_external.object({ utilization: exports_external.number().nullable().optional() }).optional(),
55712
+ seven_day: exports_external.object({
55713
+ utilization: exports_external.number().nullable().optional(),
55714
+ resets_at: exports_external.string().nullable().optional()
55715
+ }).optional(),
55668
55716
  extra_usage: exports_external.object({
55669
55717
  is_enabled: exports_external.boolean().nullable().optional(),
55670
55718
  monthly_limit: exports_external.number().nullable().optional(),
@@ -55694,6 +55742,7 @@ function parseCachedUsageData(rawJson) {
55694
55742
  sessionUsage: parsed.sessionUsage ?? undefined,
55695
55743
  sessionResetAt: parsed.sessionResetAt ?? undefined,
55696
55744
  weeklyUsage: parsed.weeklyUsage ?? undefined,
55745
+ weeklyResetAt: parsed.weeklyResetAt ?? undefined,
55697
55746
  extraUsageEnabled: parsed.extraUsageEnabled ?? undefined,
55698
55747
  extraUsageLimit: parsed.extraUsageLimit ?? undefined,
55699
55748
  extraUsageUsed: parsed.extraUsageUsed ?? undefined,
@@ -55708,8 +55757,9 @@ function parseUsageApiResponse(rawJson) {
55708
55757
  }
55709
55758
  return {
55710
55759
  sessionUsage: parsed.five_hour?.utilization ?? undefined,
55711
- sessionResetAt: parsed.five_hour?.resets_at ?? parsed.five_hour?.reset_at ?? undefined,
55760
+ sessionResetAt: parsed.five_hour?.resets_at ?? undefined,
55712
55761
  weeklyUsage: parsed.seven_day?.utilization ?? undefined,
55762
+ weeklyResetAt: parsed.seven_day?.resets_at ?? undefined,
55713
55763
  extraUsageEnabled: parsed.extra_usage?.is_enabled ?? undefined,
55714
55764
  extraUsageLimit: parsed.extra_usage?.monthly_limit ?? undefined,
55715
55765
  extraUsageUsed: parsed.extra_usage?.used_credits ?? undefined,
@@ -55890,16 +55940,16 @@ function fetchUsageData() {
55890
55940
  function clamp(value, min, max) {
55891
55941
  return Math.max(min, Math.min(max, value));
55892
55942
  }
55893
- function buildUsageWindow(resetAtMs, nowMs) {
55894
- if (!Number.isFinite(resetAtMs) || !Number.isFinite(nowMs)) {
55943
+ function buildUsageWindow(resetAtMs, nowMs, durationMs) {
55944
+ if (!Number.isFinite(resetAtMs) || !Number.isFinite(nowMs) || !Number.isFinite(durationMs) || durationMs <= 0) {
55895
55945
  return null;
55896
55946
  }
55897
- const startAtMs = resetAtMs - FIVE_HOUR_BLOCK_MS;
55898
- const elapsedMs = clamp(nowMs - startAtMs, 0, FIVE_HOUR_BLOCK_MS);
55899
- const remainingMs = FIVE_HOUR_BLOCK_MS - elapsedMs;
55900
- const elapsedPercent = elapsedMs / FIVE_HOUR_BLOCK_MS * 100;
55947
+ const startAtMs = resetAtMs - durationMs;
55948
+ const elapsedMs = clamp(nowMs - startAtMs, 0, durationMs);
55949
+ const remainingMs = durationMs - elapsedMs;
55950
+ const elapsedPercent = elapsedMs / durationMs * 100;
55901
55951
  return {
55902
- sessionDurationMs: FIVE_HOUR_BLOCK_MS,
55952
+ sessionDurationMs: durationMs,
55903
55953
  elapsedMs,
55904
55954
  remainingMs,
55905
55955
  elapsedPercent,
@@ -55914,14 +55964,14 @@ function getUsageWindowFromResetAt(sessionResetAt, nowMs = Date.now()) {
55914
55964
  if (Number.isNaN(resetAtMs)) {
55915
55965
  return null;
55916
55966
  }
55917
- return buildUsageWindow(resetAtMs, nowMs);
55967
+ return buildUsageWindow(resetAtMs, nowMs, FIVE_HOUR_BLOCK_MS);
55918
55968
  }
55919
55969
  function getUsageWindowFromBlockMetrics(blockMetrics, nowMs = Date.now()) {
55920
55970
  const startAtMs = blockMetrics.startTime.getTime();
55921
55971
  if (Number.isNaN(startAtMs)) {
55922
55972
  return null;
55923
55973
  }
55924
- return buildUsageWindow(startAtMs + FIVE_HOUR_BLOCK_MS, nowMs);
55974
+ return buildUsageWindow(startAtMs + FIVE_HOUR_BLOCK_MS, nowMs, FIVE_HOUR_BLOCK_MS);
55925
55975
  }
55926
55976
  function resolveUsageWindowWithFallback(usageData, blockMetrics, nowMs = Date.now()) {
55927
55977
  const usageWindow = getUsageWindowFromResetAt(usageData.sessionResetAt, nowMs);
@@ -55934,6 +55984,19 @@ function resolveUsageWindowWithFallback(usageData, blockMetrics, nowMs = Date.no
55934
55984
  }
55935
55985
  return getUsageWindowFromBlockMetrics(fallbackMetrics, nowMs);
55936
55986
  }
55987
+ function getWeeklyUsageWindowFromResetAt(weeklyResetAt, nowMs = Date.now()) {
55988
+ if (!weeklyResetAt) {
55989
+ return null;
55990
+ }
55991
+ const resetAtMs = Date.parse(weeklyResetAt);
55992
+ if (Number.isNaN(resetAtMs)) {
55993
+ return null;
55994
+ }
55995
+ return buildUsageWindow(resetAtMs, nowMs, SEVEN_DAY_WINDOW_MS);
55996
+ }
55997
+ function resolveWeeklyUsageWindow(usageData, nowMs = Date.now()) {
55998
+ return getWeeklyUsageWindowFromResetAt(usageData.weeklyResetAt, nowMs);
55999
+ }
55937
56000
  function formatUsageDuration(durationMs) {
55938
56001
  const clampedMs = Math.max(0, durationMs);
55939
56002
  const elapsedHours = Math.floor(clampedMs / (1000 * 60 * 60));
@@ -56648,7 +56711,7 @@ class WeeklyUsageWidget {
56648
56711
  return true;
56649
56712
  }
56650
56713
  }
56651
- // src/widgets/ResetTimer.ts
56714
+ // src/widgets/BlockResetTimer.ts
56652
56715
  function makeTimerProgressBar2(percent, width) {
56653
56716
  const clampedPercent = Math.max(0, Math.min(100, percent));
56654
56717
  const filledWidth = Math.floor(clampedPercent / 100 * width);
@@ -56656,15 +56719,15 @@ function makeTimerProgressBar2(percent, width) {
56656
56719
  return "█".repeat(filledWidth) + "░".repeat(emptyWidth);
56657
56720
  }
56658
56721
 
56659
- class ResetTimerWidget {
56722
+ class BlockResetTimerWidget {
56660
56723
  getDefaultColor() {
56661
56724
  return "brightBlue";
56662
56725
  }
56663
56726
  getDescription() {
56664
- return "Shows time remaining until current 5hr block reset";
56727
+ return "Shows time remaining until current 5hr block reset window";
56665
56728
  }
56666
56729
  getDisplayName() {
56667
- return "Reset Timer";
56730
+ return "Block Reset Timer";
56668
56731
  }
56669
56732
  getCategory() {
56670
56733
  return "Usage";
@@ -56727,6 +56790,85 @@ class ResetTimerWidget {
56727
56790
  return true;
56728
56791
  }
56729
56792
  }
56793
+ // src/widgets/WeeklyResetTimer.ts
56794
+ function makeTimerProgressBar3(percent, width) {
56795
+ const clampedPercent = Math.max(0, Math.min(100, percent));
56796
+ const filledWidth = Math.floor(clampedPercent / 100 * width);
56797
+ const emptyWidth = width - filledWidth;
56798
+ return "█".repeat(filledWidth) + "░".repeat(emptyWidth);
56799
+ }
56800
+
56801
+ class WeeklyResetTimerWidget {
56802
+ getDefaultColor() {
56803
+ return "brightBlue";
56804
+ }
56805
+ getDescription() {
56806
+ return "Shows time remaining until weekly usage reset";
56807
+ }
56808
+ getDisplayName() {
56809
+ return "Weekly Reset Timer";
56810
+ }
56811
+ getCategory() {
56812
+ return "Usage";
56813
+ }
56814
+ getEditorDisplay(item) {
56815
+ return {
56816
+ displayText: this.getDisplayName(),
56817
+ modifierText: getUsageDisplayModifierText(item)
56818
+ };
56819
+ }
56820
+ handleEditorAction(action, item) {
56821
+ if (action === "toggle-progress") {
56822
+ return cycleUsageDisplayMode(item);
56823
+ }
56824
+ if (action === "toggle-invert") {
56825
+ return toggleUsageInverted(item);
56826
+ }
56827
+ return null;
56828
+ }
56829
+ render(item, context, settings) {
56830
+ const displayMode = getUsageDisplayMode(item);
56831
+ const inverted = isUsageInverted(item);
56832
+ if (context.isPreview) {
56833
+ const previewPercent = inverted ? 90 : 10;
56834
+ if (isUsageProgressMode(displayMode)) {
56835
+ const barWidth = getUsageProgressBarWidth(displayMode);
56836
+ const progressBar = makeTimerProgressBar3(previewPercent, barWidth);
56837
+ return formatRawOrLabeledValue(item, "Weekly Reset ", `[${progressBar}] ${previewPercent.toFixed(1)}%`);
56838
+ }
56839
+ return formatRawOrLabeledValue(item, "Weekly Reset: ", "36hr 30m");
56840
+ }
56841
+ const usageData = fetchUsageData();
56842
+ const window2 = resolveWeeklyUsageWindow(usageData);
56843
+ if (!window2) {
56844
+ if (usageData.error) {
56845
+ return getUsageErrorMessage(usageData.error);
56846
+ }
56847
+ return null;
56848
+ }
56849
+ if (isUsageProgressMode(displayMode)) {
56850
+ const barWidth = getUsageProgressBarWidth(displayMode);
56851
+ const percent = inverted ? window2.remainingPercent : window2.elapsedPercent;
56852
+ const progressBar = makeTimerProgressBar3(percent, barWidth);
56853
+ const percentage = percent.toFixed(1);
56854
+ return formatRawOrLabeledValue(item, "Weekly Reset ", `[${progressBar}] ${percentage}%`);
56855
+ }
56856
+ const remainingTime = formatUsageDuration(window2.remainingMs);
56857
+ return formatRawOrLabeledValue(item, "Weekly Reset: ", remainingTime);
56858
+ }
56859
+ getCustomKeybinds() {
56860
+ return [
56861
+ { key: "p", label: "(p)rogress toggle", action: "toggle-progress" },
56862
+ { key: "v", label: "in(v)ert fill", action: "toggle-invert" }
56863
+ ];
56864
+ }
56865
+ supportsRawValue() {
56866
+ return true;
56867
+ }
56868
+ supportsColors(item) {
56869
+ return true;
56870
+ }
56871
+ }
56730
56872
  // src/widgets/ContextBar.ts
56731
56873
  function getDisplayMode(item) {
56732
56874
  return item.metadata?.display === "progress" ? "progress" : "progress-short";
@@ -56784,9 +56926,8 @@ class ContextBarWidget {
56784
56926
  used = context.tokenMetrics.contextLength;
56785
56927
  }
56786
56928
  if (total === null && context.tokenMetrics) {
56787
- const model = context.data?.model;
56788
- const modelId = typeof model === "string" ? model : model?.id;
56789
- total = getContextConfig(modelId).maxTokens;
56929
+ const modelIdentifier = getModelContextIdentifier(context.data?.model);
56930
+ total = getContextConfig(modelIdentifier).maxTokens;
56790
56931
  }
56791
56932
  if (used === null || total === null || total <= 0) {
56792
56933
  return null;
@@ -57035,7 +57176,8 @@ var widgetRegistry = new Map([
57035
57176
  ["free-memory", new FreeMemoryWidget],
57036
57177
  ["session-usage", new SessionUsageWidget],
57037
57178
  ["weekly-usage", new WeeklyUsageWidget],
57038
- ["reset-timer", new ResetTimerWidget],
57179
+ ["reset-timer", new BlockResetTimerWidget],
57180
+ ["weekly-reset-timer", new WeeklyResetTimerWidget],
57039
57181
  ["context-bar", new ContextBarWidget]
57040
57182
  ]);
57041
57183
  function getWidget(type) {
@@ -57204,6 +57346,111 @@ var ConfirmDialog = ({ message, onConfirm, onCancel, inline = false }) => {
57204
57346
  }, undefined, true, undefined, this);
57205
57347
  };
57206
57348
 
57349
+ // src/tui/components/color-menu/mutations.ts
57350
+ function updateWidgetById(widgets, widgetId, updater) {
57351
+ return widgets.map((widget) => widget.id === widgetId ? updater(widget) : widget);
57352
+ }
57353
+ function setWidgetColor(widgets, widgetId, color, editingBackground) {
57354
+ return updateWidgetById(widgets, widgetId, (widget) => {
57355
+ if (editingBackground) {
57356
+ return {
57357
+ ...widget,
57358
+ backgroundColor: color
57359
+ };
57360
+ }
57361
+ return {
57362
+ ...widget,
57363
+ color
57364
+ };
57365
+ });
57366
+ }
57367
+ function toggleWidgetBold(widgets, widgetId) {
57368
+ return updateWidgetById(widgets, widgetId, (widget) => ({
57369
+ ...widget,
57370
+ bold: !widget.bold
57371
+ }));
57372
+ }
57373
+ function resetWidgetStyling(widgets, widgetId) {
57374
+ return updateWidgetById(widgets, widgetId, (widget) => {
57375
+ const {
57376
+ color,
57377
+ backgroundColor,
57378
+ bold,
57379
+ ...restWidget
57380
+ } = widget;
57381
+ return restWidget;
57382
+ });
57383
+ }
57384
+ function clearAllWidgetStyling(widgets) {
57385
+ return widgets.map((widget) => {
57386
+ const {
57387
+ color,
57388
+ backgroundColor,
57389
+ bold,
57390
+ ...restWidget
57391
+ } = widget;
57392
+ return restWidget;
57393
+ });
57394
+ }
57395
+ function getDefaultForegroundColor(widget) {
57396
+ if (widget.type === "separator" || widget.type === "flex-separator") {
57397
+ return "white";
57398
+ }
57399
+ const widgetImpl = getWidget(widget.type);
57400
+ return widgetImpl ? widgetImpl.getDefaultColor() : "white";
57401
+ }
57402
+ function getNextIndex(currentIndex, length, direction) {
57403
+ if (direction === "right") {
57404
+ return (currentIndex + 1) % length;
57405
+ }
57406
+ return currentIndex === 0 ? length - 1 : currentIndex - 1;
57407
+ }
57408
+ function cycleWidgetColor({
57409
+ widgets,
57410
+ widgetId,
57411
+ direction,
57412
+ editingBackground,
57413
+ colors,
57414
+ backgroundColors
57415
+ }) {
57416
+ return updateWidgetById(widgets, widgetId, (widget) => {
57417
+ if (editingBackground) {
57418
+ if (backgroundColors.length === 0) {
57419
+ return widget;
57420
+ }
57421
+ const currentBgColor = widget.backgroundColor ?? "";
57422
+ let currentBgColorIndex = backgroundColors.indexOf(currentBgColor);
57423
+ if (currentBgColorIndex === -1) {
57424
+ currentBgColorIndex = 0;
57425
+ }
57426
+ const nextBgColorIndex = getNextIndex(currentBgColorIndex, backgroundColors.length, direction);
57427
+ const nextBgColor = backgroundColors[nextBgColorIndex];
57428
+ return {
57429
+ ...widget,
57430
+ backgroundColor: nextBgColor === "" ? undefined : nextBgColor
57431
+ };
57432
+ }
57433
+ if (colors.length === 0) {
57434
+ return widget;
57435
+ }
57436
+ const defaultColor = getDefaultForegroundColor(widget);
57437
+ let currentColor = widget.color ?? defaultColor;
57438
+ if (currentColor === "dim") {
57439
+ currentColor = defaultColor;
57440
+ }
57441
+ let currentColorIndex = colors.indexOf(currentColor);
57442
+ if (currentColorIndex === -1) {
57443
+ currentColorIndex = 0;
57444
+ }
57445
+ const nextColorIndex = getNextIndex(currentColorIndex, colors.length, direction);
57446
+ const nextColor = colors[nextColorIndex];
57447
+ return {
57448
+ ...widget,
57449
+ color: nextColor
57450
+ };
57451
+ });
57452
+ }
57453
+
57207
57454
  // src/tui/components/ColorMenu.tsx
57208
57455
  var jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
57209
57456
  var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
@@ -57244,16 +57491,7 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
57244
57491
  const hexColor = `hex:${hexInput}`;
57245
57492
  const selectedWidget2 = colorableWidgets.find((widget) => widget.id === highlightedItemId);
57246
57493
  if (selectedWidget2) {
57247
- const newItems = widgets.map((widget) => {
57248
- if (widget.id === highlightedItemId) {
57249
- if (editingBackground) {
57250
- return { ...widget, backgroundColor: hexColor };
57251
- } else {
57252
- return { ...widget, color: hexColor };
57253
- }
57254
- }
57255
- return widget;
57256
- });
57494
+ const newItems = setWidgetColor(widgets, selectedWidget2.id, hexColor, editingBackground);
57257
57495
  onUpdate(newItems);
57258
57496
  }
57259
57497
  setHexInputMode(false);
@@ -57282,16 +57520,7 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
57282
57520
  const ansiColor = `ansi256:${code}`;
57283
57521
  const selectedWidget2 = colorableWidgets.find((widget) => widget.id === highlightedItemId);
57284
57522
  if (selectedWidget2) {
57285
- const newItems = widgets.map((widget) => {
57286
- if (widget.id === highlightedItemId) {
57287
- if (editingBackground) {
57288
- return { ...widget, backgroundColor: ansiColor };
57289
- } else {
57290
- return { ...widget, color: ansiColor };
57291
- }
57292
- }
57293
- return widget;
57294
- });
57523
+ const newItems = setWidgetColor(widgets, selectedWidget2.id, ansiColor, editingBackground);
57295
57524
  onUpdate(newItems);
57296
57525
  setAnsi256InputMode(false);
57297
57526
  setAnsi256Input("");
@@ -57341,12 +57570,7 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
57341
57570
  if (highlightedItemId && highlightedItemId !== "back") {
57342
57571
  const selectedWidget2 = colorableWidgets.find((widget) => widget.id === highlightedItemId);
57343
57572
  if (selectedWidget2) {
57344
- const newItems = widgets.map((widget) => {
57345
- if (widget.id === selectedWidget2.id) {
57346
- return { ...widget, bold: !widget.bold };
57347
- }
57348
- return widget;
57349
- });
57573
+ const newItems = toggleWidgetBold(widgets, selectedWidget2.id);
57350
57574
  onUpdate(newItems);
57351
57575
  }
57352
57576
  }
@@ -57354,13 +57578,7 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
57354
57578
  if (highlightedItemId && highlightedItemId !== "back") {
57355
57579
  const selectedWidget2 = colorableWidgets.find((widget) => widget.id === highlightedItemId);
57356
57580
  if (selectedWidget2) {
57357
- const newItems = widgets.map((widget) => {
57358
- if (widget.id === selectedWidget2.id) {
57359
- const { color, backgroundColor, bold, ...restWidget } = widget;
57360
- return restWidget;
57361
- }
57362
- return widget;
57363
- });
57581
+ const newItems = resetWidgetStyling(widgets, selectedWidget2.id);
57364
57582
  onUpdate(newItems);
57365
57583
  }
57366
57584
  }
@@ -57370,47 +57588,13 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
57370
57588
  if (highlightedItemId && highlightedItemId !== "back") {
57371
57589
  const selectedWidget2 = colorableWidgets.find((widget) => widget.id === highlightedItemId);
57372
57590
  if (selectedWidget2) {
57373
- const newItems = widgets.map((widget) => {
57374
- if (widget.id === selectedWidget2.id) {
57375
- if (editingBackground) {
57376
- const currentBgColor = widget.backgroundColor ?? "";
57377
- let currentBgColorIndex = bgColors.indexOf(currentBgColor);
57378
- if (currentBgColorIndex === -1)
57379
- currentBgColorIndex = 0;
57380
- let nextBgColorIndex;
57381
- if (key.rightArrow) {
57382
- nextBgColorIndex = (currentBgColorIndex + 1) % bgColors.length;
57383
- } else {
57384
- nextBgColorIndex = currentBgColorIndex === 0 ? bgColors.length - 1 : currentBgColorIndex - 1;
57385
- }
57386
- const nextBgColor = bgColors[nextBgColorIndex];
57387
- return { ...widget, backgroundColor: nextBgColor === "" ? undefined : nextBgColor };
57388
- } else {
57389
- let defaultColor = "white";
57390
- if (widget.type !== "separator" && widget.type !== "flex-separator") {
57391
- const widgetImpl = getWidget(widget.type);
57392
- if (widgetImpl) {
57393
- defaultColor = widgetImpl.getDefaultColor();
57394
- }
57395
- }
57396
- let currentColor2 = widget.color ?? defaultColor;
57397
- if (currentColor2 === "dim") {
57398
- currentColor2 = defaultColor;
57399
- }
57400
- let currentColorIndex = colors.indexOf(currentColor2);
57401
- if (currentColorIndex === -1)
57402
- currentColorIndex = 0;
57403
- let nextColorIndex;
57404
- if (key.rightArrow) {
57405
- nextColorIndex = (currentColorIndex + 1) % colors.length;
57406
- } else {
57407
- nextColorIndex = currentColorIndex === 0 ? colors.length - 1 : currentColorIndex - 1;
57408
- }
57409
- const nextColor = colors[nextColorIndex];
57410
- return { ...widget, color: nextColor };
57411
- }
57412
- }
57413
- return widget;
57591
+ const newItems = cycleWidgetColor({
57592
+ widgets,
57593
+ widgetId: selectedWidget2.id,
57594
+ direction: key.rightArrow ? "right" : "left",
57595
+ editingBackground,
57596
+ colors,
57597
+ backgroundColors: bgColors
57414
57598
  });
57415
57599
  onUpdate(newItems);
57416
57600
  }
@@ -57566,10 +57750,7 @@ var ColorMenu = ({ widgets, lineIndex, settings, onUpdate, onBack }) => {
57566
57750
  children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ConfirmDialog, {
57567
57751
  inline: true,
57568
57752
  onConfirm: () => {
57569
- const newItems = widgets.map((widget) => {
57570
- const { color, backgroundColor, bold, ...restWidget } = widget;
57571
- return restWidget;
57572
- });
57753
+ const newItems = clearAllWidgetStyling(widgets);
57573
57754
  onUpdate(newItems);
57574
57755
  setShowClearConfirm(false);
57575
57756
  },
@@ -58257,6 +58438,317 @@ var InstallMenu = ({
58257
58438
  };
58258
58439
  // src/tui/components/ItemsEditor.tsx
58259
58440
  var import_react37 = __toESM(require_react(), 1);
58441
+
58442
+ // src/tui/components/items-editor/input-handlers.ts
58443
+ function setPickerState(setWidgetPicker, normalizeState, updater) {
58444
+ setWidgetPicker((prev) => {
58445
+ if (!prev) {
58446
+ return prev;
58447
+ }
58448
+ return normalizeState(updater(prev));
58449
+ });
58450
+ }
58451
+ function getPickerCategories(widgetCategories) {
58452
+ return [...widgetCategories];
58453
+ }
58454
+ function normalizePickerState(state, widgetCatalog, widgetCategories) {
58455
+ const filteredCategories = getPickerCategories(widgetCategories);
58456
+ const selectedCategory = state.selectedCategory && filteredCategories.includes(state.selectedCategory) ? state.selectedCategory : filteredCategories[0] ?? null;
58457
+ const hasTopLevelSearch = state.level === "category" && state.categoryQuery.trim().length > 0;
58458
+ const effectiveCategory = hasTopLevelSearch ? "All" : selectedCategory ?? "All";
58459
+ const effectiveQuery = hasTopLevelSearch ? state.categoryQuery : state.widgetQuery;
58460
+ const filteredWidgets = filterWidgetCatalog(widgetCatalog, effectiveCategory, effectiveQuery);
58461
+ const hasSelectedType = state.selectedType ? filteredWidgets.some((entry) => entry.type === state.selectedType) : false;
58462
+ return {
58463
+ ...state,
58464
+ selectedCategory,
58465
+ selectedType: hasSelectedType ? state.selectedType : filteredWidgets[0]?.type ?? null
58466
+ };
58467
+ }
58468
+ function getPickerViewState(widgetPicker, widgetCatalog, widgetCategories) {
58469
+ const filteredCategories = getPickerCategories(widgetCategories);
58470
+ const selectedCategory = widgetPicker.selectedCategory && filteredCategories.includes(widgetPicker.selectedCategory) ? widgetPicker.selectedCategory : filteredCategories[0] ?? null;
58471
+ const hasTopLevelSearch = widgetPicker.level === "category" && widgetPicker.categoryQuery.trim().length > 0;
58472
+ const topLevelSearchEntries = hasTopLevelSearch ? filterWidgetCatalog(widgetCatalog, "All", widgetPicker.categoryQuery) : [];
58473
+ const topLevelSelectedEntry = topLevelSearchEntries.find((entry) => entry.type === widgetPicker.selectedType) ?? topLevelSearchEntries[0];
58474
+ const filteredWidgets = filterWidgetCatalog(widgetCatalog, selectedCategory ?? "All", widgetPicker.widgetQuery);
58475
+ const selectedEntry = filteredWidgets.find((entry) => entry.type === widgetPicker.selectedType) ?? filteredWidgets[0];
58476
+ return {
58477
+ filteredCategories,
58478
+ selectedCategory,
58479
+ hasTopLevelSearch,
58480
+ topLevelSearchEntries,
58481
+ topLevelSelectedEntry,
58482
+ filteredWidgets,
58483
+ selectedEntry
58484
+ };
58485
+ }
58486
+ function handlePickerInputMode({
58487
+ input,
58488
+ key,
58489
+ widgetPicker,
58490
+ widgetCatalog,
58491
+ widgetCategories,
58492
+ setWidgetPicker,
58493
+ applyWidgetPickerSelection
58494
+ }) {
58495
+ const normalizeState = (state) => normalizePickerState(state, widgetCatalog, widgetCategories);
58496
+ const {
58497
+ filteredCategories,
58498
+ selectedCategory,
58499
+ hasTopLevelSearch,
58500
+ topLevelSearchEntries,
58501
+ topLevelSelectedEntry,
58502
+ filteredWidgets,
58503
+ selectedEntry
58504
+ } = getPickerViewState(widgetPicker, widgetCatalog, widgetCategories);
58505
+ if (widgetPicker.level === "category") {
58506
+ if (key.escape) {
58507
+ if (widgetPicker.categoryQuery.length > 0) {
58508
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58509
+ ...prev,
58510
+ categoryQuery: ""
58511
+ }));
58512
+ } else {
58513
+ setWidgetPicker(null);
58514
+ }
58515
+ } else if (key.return) {
58516
+ if (hasTopLevelSearch) {
58517
+ if (topLevelSelectedEntry) {
58518
+ applyWidgetPickerSelection(topLevelSelectedEntry.type);
58519
+ }
58520
+ } else if (selectedCategory) {
58521
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58522
+ ...prev,
58523
+ level: "widget",
58524
+ selectedCategory
58525
+ }));
58526
+ }
58527
+ } else if (key.upArrow || key.downArrow) {
58528
+ if (hasTopLevelSearch) {
58529
+ if (topLevelSearchEntries.length === 0) {
58530
+ return;
58531
+ }
58532
+ let currentIndex = topLevelSearchEntries.findIndex((entry) => entry.type === widgetPicker.selectedType);
58533
+ if (currentIndex === -1) {
58534
+ currentIndex = 0;
58535
+ }
58536
+ const nextIndex = key.downArrow ? Math.min(topLevelSearchEntries.length - 1, currentIndex + 1) : Math.max(0, currentIndex - 1);
58537
+ const nextType = topLevelSearchEntries[nextIndex]?.type ?? null;
58538
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58539
+ ...prev,
58540
+ selectedType: nextType
58541
+ }));
58542
+ } else {
58543
+ if (filteredCategories.length === 0) {
58544
+ return;
58545
+ }
58546
+ let currentIndex = filteredCategories.findIndex((category) => category === selectedCategory);
58547
+ if (currentIndex === -1) {
58548
+ currentIndex = 0;
58549
+ }
58550
+ const nextIndex = key.downArrow ? Math.min(filteredCategories.length - 1, currentIndex + 1) : Math.max(0, currentIndex - 1);
58551
+ const nextCategory = filteredCategories[nextIndex] ?? null;
58552
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58553
+ ...prev,
58554
+ selectedCategory: nextCategory
58555
+ }));
58556
+ }
58557
+ } else if (key.backspace || key.delete) {
58558
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58559
+ ...prev,
58560
+ categoryQuery: prev.categoryQuery.slice(0, -1)
58561
+ }));
58562
+ } else if (input && !key.ctrl && !key.meta && !key.tab) {
58563
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58564
+ ...prev,
58565
+ categoryQuery: prev.categoryQuery + input
58566
+ }));
58567
+ }
58568
+ } else {
58569
+ if (key.escape) {
58570
+ if (widgetPicker.widgetQuery.length > 0) {
58571
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58572
+ ...prev,
58573
+ widgetQuery: ""
58574
+ }));
58575
+ } else {
58576
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58577
+ ...prev,
58578
+ level: "category"
58579
+ }));
58580
+ }
58581
+ } else if (key.return) {
58582
+ if (selectedEntry) {
58583
+ applyWidgetPickerSelection(selectedEntry.type);
58584
+ }
58585
+ } else if (key.upArrow || key.downArrow) {
58586
+ if (filteredWidgets.length === 0) {
58587
+ return;
58588
+ }
58589
+ let currentIndex = filteredWidgets.findIndex((entry) => entry.type === widgetPicker.selectedType);
58590
+ if (currentIndex === -1) {
58591
+ currentIndex = 0;
58592
+ }
58593
+ const nextIndex = key.downArrow ? Math.min(filteredWidgets.length - 1, currentIndex + 1) : Math.max(0, currentIndex - 1);
58594
+ const nextType = filteredWidgets[nextIndex]?.type ?? null;
58595
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58596
+ ...prev,
58597
+ selectedType: nextType
58598
+ }));
58599
+ } else if (key.backspace || key.delete) {
58600
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58601
+ ...prev,
58602
+ widgetQuery: prev.widgetQuery.slice(0, -1)
58603
+ }));
58604
+ } else if (input && !key.ctrl && !key.meta && !key.tab) {
58605
+ setPickerState(setWidgetPicker, normalizeState, (prev) => ({
58606
+ ...prev,
58607
+ widgetQuery: prev.widgetQuery + input
58608
+ }));
58609
+ }
58610
+ }
58611
+ }
58612
+ function handleMoveInputMode({
58613
+ key,
58614
+ widgets,
58615
+ selectedIndex,
58616
+ onUpdate,
58617
+ setSelectedIndex,
58618
+ setMoveMode
58619
+ }) {
58620
+ if (key.upArrow && selectedIndex > 0) {
58621
+ const newWidgets = [...widgets];
58622
+ const temp = newWidgets[selectedIndex];
58623
+ const prev = newWidgets[selectedIndex - 1];
58624
+ if (temp && prev) {
58625
+ [newWidgets[selectedIndex], newWidgets[selectedIndex - 1]] = [prev, temp];
58626
+ }
58627
+ onUpdate(newWidgets);
58628
+ setSelectedIndex(selectedIndex - 1);
58629
+ } else if (key.downArrow && selectedIndex < widgets.length - 1) {
58630
+ const newWidgets = [...widgets];
58631
+ const temp = newWidgets[selectedIndex];
58632
+ const next = newWidgets[selectedIndex + 1];
58633
+ if (temp && next) {
58634
+ [newWidgets[selectedIndex], newWidgets[selectedIndex + 1]] = [next, temp];
58635
+ }
58636
+ onUpdate(newWidgets);
58637
+ setSelectedIndex(selectedIndex + 1);
58638
+ } else if (key.escape || key.return) {
58639
+ setMoveMode(false);
58640
+ }
58641
+ }
58642
+ function handleNormalInputMode({
58643
+ input,
58644
+ key,
58645
+ widgets,
58646
+ selectedIndex,
58647
+ separatorChars,
58648
+ onBack,
58649
+ onUpdate,
58650
+ setSelectedIndex,
58651
+ setMoveMode,
58652
+ setShowClearConfirm,
58653
+ openWidgetPicker,
58654
+ getVisibleCustomKeybinds,
58655
+ setCustomEditorWidget
58656
+ }) {
58657
+ if (key.upArrow && widgets.length > 0) {
58658
+ setSelectedIndex(Math.max(0, selectedIndex - 1));
58659
+ } else if (key.downArrow && widgets.length > 0) {
58660
+ setSelectedIndex(Math.min(widgets.length - 1, selectedIndex + 1));
58661
+ } else if (key.leftArrow && widgets.length > 0) {
58662
+ openWidgetPicker("change");
58663
+ } else if (key.rightArrow && widgets.length > 0) {
58664
+ openWidgetPicker("change");
58665
+ } else if (key.return && widgets.length > 0) {
58666
+ setMoveMode(true);
58667
+ } else if (input === "a") {
58668
+ openWidgetPicker("add");
58669
+ } else if (input === "i") {
58670
+ openWidgetPicker("insert");
58671
+ } else if (input === "d" && widgets.length > 0) {
58672
+ const newWidgets = widgets.filter((_, i) => i !== selectedIndex);
58673
+ onUpdate(newWidgets);
58674
+ if (selectedIndex >= newWidgets.length && selectedIndex > 0) {
58675
+ setSelectedIndex(selectedIndex - 1);
58676
+ }
58677
+ } else if (input === "c") {
58678
+ if (widgets.length > 0) {
58679
+ setShowClearConfirm(true);
58680
+ }
58681
+ } else if (input === " " && widgets.length > 0) {
58682
+ const currentWidget = widgets[selectedIndex];
58683
+ if (currentWidget && currentWidget.type === "separator") {
58684
+ const currentChar = currentWidget.character ?? "|";
58685
+ const currentCharIndex = separatorChars.indexOf(currentChar);
58686
+ const nextChar = separatorChars[(currentCharIndex + 1) % separatorChars.length];
58687
+ const newWidgets = [...widgets];
58688
+ newWidgets[selectedIndex] = { ...currentWidget, character: nextChar };
58689
+ onUpdate(newWidgets);
58690
+ }
58691
+ } else if (input === "r" && widgets.length > 0) {
58692
+ const currentWidget = widgets[selectedIndex];
58693
+ if (currentWidget && currentWidget.type !== "separator" && currentWidget.type !== "flex-separator") {
58694
+ const widgetImpl = getWidget(currentWidget.type);
58695
+ if (!widgetImpl?.supportsRawValue()) {
58696
+ return;
58697
+ }
58698
+ const newWidgets = [...widgets];
58699
+ newWidgets[selectedIndex] = { ...currentWidget, rawValue: !currentWidget.rawValue };
58700
+ onUpdate(newWidgets);
58701
+ }
58702
+ } else if (input === "m" && widgets.length > 0) {
58703
+ const currentWidget = widgets[selectedIndex];
58704
+ if (currentWidget && selectedIndex < widgets.length - 1 && currentWidget.type !== "separator" && currentWidget.type !== "flex-separator") {
58705
+ const newWidgets = [...widgets];
58706
+ let nextMergeState;
58707
+ if (currentWidget.merge === undefined) {
58708
+ nextMergeState = true;
58709
+ } else if (currentWidget.merge === true) {
58710
+ nextMergeState = "no-padding";
58711
+ } else {
58712
+ nextMergeState = undefined;
58713
+ }
58714
+ if (nextMergeState === undefined) {
58715
+ const { merge: merge2, ...rest } = currentWidget;
58716
+ newWidgets[selectedIndex] = rest;
58717
+ } else {
58718
+ newWidgets[selectedIndex] = { ...currentWidget, merge: nextMergeState };
58719
+ }
58720
+ onUpdate(newWidgets);
58721
+ }
58722
+ } else if (key.escape) {
58723
+ onBack();
58724
+ } else if (widgets.length > 0) {
58725
+ const currentWidget = widgets[selectedIndex];
58726
+ if (currentWidget && currentWidget.type !== "separator" && currentWidget.type !== "flex-separator") {
58727
+ const widgetImpl = getWidget(currentWidget.type);
58728
+ if (!widgetImpl?.getCustomKeybinds) {
58729
+ return;
58730
+ }
58731
+ const customKeybinds = getVisibleCustomKeybinds(widgetImpl, currentWidget);
58732
+ const matchedKeybind = customKeybinds.find((kb) => kb.key === input);
58733
+ if (matchedKeybind && !key.ctrl) {
58734
+ if (widgetImpl.handleEditorAction) {
58735
+ const updatedWidget = widgetImpl.handleEditorAction(matchedKeybind.action, currentWidget);
58736
+ if (updatedWidget) {
58737
+ const newWidgets = [...widgets];
58738
+ newWidgets[selectedIndex] = updatedWidget;
58739
+ onUpdate(newWidgets);
58740
+ } else if (widgetImpl.renderEditor) {
58741
+ setCustomEditorWidget({ widget: currentWidget, impl: widgetImpl, action: matchedKeybind.action });
58742
+ }
58743
+ } else if (widgetImpl.renderEditor) {
58744
+ setCustomEditorWidget({ widget: currentWidget, impl: widgetImpl, action: matchedKeybind.action });
58745
+ }
58746
+ }
58747
+ }
58748
+ }
58749
+ }
58750
+
58751
+ // src/tui/components/ItemsEditor.tsx
58260
58752
  var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
58261
58753
  var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
58262
58754
  const [selectedIndex, setSelectedIndex] = import_react37.useState(0);
@@ -58292,23 +58784,6 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
58292
58784
  const handleEditorCancel = () => {
58293
58785
  setCustomEditorWidget(null);
58294
58786
  };
58295
- const getFilteredCategories = (query) => {
58296
- return [...widgetCategories];
58297
- };
58298
- const normalizePickerState = (state) => {
58299
- const filteredCategories = getFilteredCategories(state.categoryQuery);
58300
- const selectedCategory = state.selectedCategory && filteredCategories.includes(state.selectedCategory) ? state.selectedCategory : filteredCategories[0] ?? null;
58301
- const hasTopLevelSearch = state.level === "category" && state.categoryQuery.trim().length > 0;
58302
- const effectiveCategory = hasTopLevelSearch ? "All" : selectedCategory ?? "All";
58303
- const effectiveQuery = hasTopLevelSearch ? state.categoryQuery : state.widgetQuery;
58304
- const filteredWidgets = filterWidgetCatalog(widgetCatalog, effectiveCategory, effectiveQuery);
58305
- const hasSelectedType = state.selectedType ? filteredWidgets.some((entry) => entry.type === state.selectedType) : false;
58306
- return {
58307
- ...state,
58308
- selectedCategory,
58309
- selectedType: hasSelectedType ? state.selectedType : filteredWidgets[0]?.type ?? null
58310
- };
58311
- };
58312
58787
  const shouldShowCustomKeybind = (widget, keybind) => {
58313
58788
  if (keybind.action !== "toggle-invert") {
58314
58789
  return true;
@@ -58335,7 +58810,7 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
58335
58810
  categoryQuery: "",
58336
58811
  widgetQuery: "",
58337
58812
  selectedType
58338
- }));
58813
+ }, widgetCatalog, widgetCategories));
58339
58814
  };
58340
58815
  const applyWidgetPickerSelection = (selectedType) => {
58341
58816
  if (!widgetPicker) {
@@ -58371,238 +58846,43 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
58371
58846
  return;
58372
58847
  }
58373
58848
  if (widgetPicker) {
58374
- const filteredCategories = getFilteredCategories(widgetPicker.categoryQuery);
58375
- const selectedCategory = widgetPicker.selectedCategory && filteredCategories.includes(widgetPicker.selectedCategory) ? widgetPicker.selectedCategory : filteredCategories[0] ?? null;
58376
- const hasTopLevelSearch = widgetPicker.level === "category" && widgetPicker.categoryQuery.trim().length > 0;
58377
- const topLevelSearchEntries2 = hasTopLevelSearch ? filterWidgetCatalog(widgetCatalog, "All", widgetPicker.categoryQuery) : [];
58378
- const topLevelSelectedEntry = topLevelSearchEntries2.find((entry) => entry.type === widgetPicker.selectedType) ?? topLevelSearchEntries2[0];
58379
- const filteredWidgets = filterWidgetCatalog(widgetCatalog, selectedCategory ?? "All", widgetPicker.widgetQuery);
58380
- const selectedEntry = filteredWidgets.find((entry) => entry.type === widgetPicker.selectedType) ?? filteredWidgets[0];
58381
- if (widgetPicker.level === "category") {
58382
- if (key.escape) {
58383
- if (widgetPicker.categoryQuery.length > 0) {
58384
- setWidgetPicker((prev) => prev ? normalizePickerState({
58385
- ...prev,
58386
- categoryQuery: ""
58387
- }) : prev);
58388
- } else {
58389
- setWidgetPicker(null);
58390
- }
58391
- } else if (key.return) {
58392
- if (hasTopLevelSearch) {
58393
- if (topLevelSelectedEntry) {
58394
- applyWidgetPickerSelection(topLevelSelectedEntry.type);
58395
- }
58396
- } else if (selectedCategory) {
58397
- setWidgetPicker((prev) => prev ? normalizePickerState({
58398
- ...prev,
58399
- level: "widget",
58400
- selectedCategory
58401
- }) : prev);
58402
- }
58403
- } else if (key.upArrow || key.downArrow) {
58404
- if (hasTopLevelSearch) {
58405
- if (topLevelSearchEntries2.length === 0) {
58406
- return;
58407
- }
58408
- let currentIndex = topLevelSearchEntries2.findIndex((entry) => entry.type === widgetPicker.selectedType);
58409
- if (currentIndex === -1) {
58410
- currentIndex = 0;
58411
- }
58412
- const nextIndex = key.downArrow ? Math.min(topLevelSearchEntries2.length - 1, currentIndex + 1) : Math.max(0, currentIndex - 1);
58413
- const nextType = topLevelSearchEntries2[nextIndex]?.type ?? null;
58414
- setWidgetPicker((prev) => prev ? normalizePickerState({
58415
- ...prev,
58416
- selectedType: nextType
58417
- }) : prev);
58418
- } else {
58419
- if (filteredCategories.length === 0) {
58420
- return;
58421
- }
58422
- let currentIndex = filteredCategories.findIndex((category) => category === selectedCategory);
58423
- if (currentIndex === -1) {
58424
- currentIndex = 0;
58425
- }
58426
- const nextIndex = key.downArrow ? Math.min(filteredCategories.length - 1, currentIndex + 1) : Math.max(0, currentIndex - 1);
58427
- const nextCategory = filteredCategories[nextIndex] ?? null;
58428
- setWidgetPicker((prev) => prev ? normalizePickerState({
58429
- ...prev,
58430
- selectedCategory: nextCategory
58431
- }) : prev);
58432
- }
58433
- } else if (key.backspace || key.delete) {
58434
- setWidgetPicker((prev) => prev ? normalizePickerState({
58435
- ...prev,
58436
- categoryQuery: prev.categoryQuery.slice(0, -1)
58437
- }) : prev);
58438
- } else if (input && !key.ctrl && !key.meta && !key.tab) {
58439
- setWidgetPicker((prev) => prev ? normalizePickerState({
58440
- ...prev,
58441
- categoryQuery: prev.categoryQuery + input
58442
- }) : prev);
58443
- }
58444
- } else {
58445
- if (key.escape) {
58446
- if (widgetPicker.widgetQuery.length > 0) {
58447
- setWidgetPicker((prev) => prev ? normalizePickerState({
58448
- ...prev,
58449
- widgetQuery: ""
58450
- }) : prev);
58451
- } else {
58452
- setWidgetPicker((prev) => prev ? normalizePickerState({
58453
- ...prev,
58454
- level: "category"
58455
- }) : prev);
58456
- }
58457
- } else if (key.return) {
58458
- if (selectedEntry) {
58459
- applyWidgetPickerSelection(selectedEntry.type);
58460
- }
58461
- } else if (key.upArrow || key.downArrow) {
58462
- if (filteredWidgets.length === 0) {
58463
- return;
58464
- }
58465
- let currentIndex = filteredWidgets.findIndex((entry) => entry.type === widgetPicker.selectedType);
58466
- if (currentIndex === -1) {
58467
- currentIndex = 0;
58468
- }
58469
- const nextIndex = key.downArrow ? Math.min(filteredWidgets.length - 1, currentIndex + 1) : Math.max(0, currentIndex - 1);
58470
- const nextType = filteredWidgets[nextIndex]?.type ?? null;
58471
- setWidgetPicker((prev) => prev ? normalizePickerState({
58472
- ...prev,
58473
- selectedType: nextType
58474
- }) : prev);
58475
- } else if (key.backspace || key.delete) {
58476
- setWidgetPicker((prev) => prev ? normalizePickerState({
58477
- ...prev,
58478
- widgetQuery: prev.widgetQuery.slice(0, -1)
58479
- }) : prev);
58480
- } else if (input && !key.ctrl && !key.meta && !key.tab) {
58481
- setWidgetPicker((prev) => prev ? normalizePickerState({
58482
- ...prev,
58483
- widgetQuery: prev.widgetQuery + input
58484
- }) : prev);
58485
- }
58486
- }
58849
+ handlePickerInputMode({
58850
+ input,
58851
+ key,
58852
+ widgetPicker,
58853
+ widgetCatalog,
58854
+ widgetCategories,
58855
+ setWidgetPicker,
58856
+ applyWidgetPickerSelection
58857
+ });
58487
58858
  return;
58488
58859
  }
58489
58860
  if (moveMode) {
58490
- if (key.upArrow && selectedIndex > 0) {
58491
- const newWidgets = [...widgets];
58492
- const temp = newWidgets[selectedIndex];
58493
- const prev = newWidgets[selectedIndex - 1];
58494
- if (temp && prev) {
58495
- [newWidgets[selectedIndex], newWidgets[selectedIndex - 1]] = [prev, temp];
58496
- }
58497
- onUpdate(newWidgets);
58498
- setSelectedIndex(selectedIndex - 1);
58499
- } else if (key.downArrow && selectedIndex < widgets.length - 1) {
58500
- const newWidgets = [...widgets];
58501
- const temp = newWidgets[selectedIndex];
58502
- const next = newWidgets[selectedIndex + 1];
58503
- if (temp && next) {
58504
- [newWidgets[selectedIndex], newWidgets[selectedIndex + 1]] = [next, temp];
58505
- }
58506
- onUpdate(newWidgets);
58507
- setSelectedIndex(selectedIndex + 1);
58508
- } else if (key.escape || key.return) {
58509
- setMoveMode(false);
58510
- }
58511
- } else {
58512
- if (key.upArrow && widgets.length > 0) {
58513
- setSelectedIndex(Math.max(0, selectedIndex - 1));
58514
- } else if (key.downArrow && widgets.length > 0) {
58515
- setSelectedIndex(Math.min(widgets.length - 1, selectedIndex + 1));
58516
- } else if (key.leftArrow && widgets.length > 0) {
58517
- openWidgetPicker("change");
58518
- } else if (key.rightArrow && widgets.length > 0) {
58519
- openWidgetPicker("change");
58520
- } else if (key.return && widgets.length > 0) {
58521
- setMoveMode(true);
58522
- } else if (input === "a") {
58523
- openWidgetPicker("add");
58524
- } else if (input === "i") {
58525
- openWidgetPicker("insert");
58526
- } else if (input === "d" && widgets.length > 0) {
58527
- const newWidgets = widgets.filter((_, i) => i !== selectedIndex);
58528
- onUpdate(newWidgets);
58529
- if (selectedIndex >= newWidgets.length && selectedIndex > 0) {
58530
- setSelectedIndex(selectedIndex - 1);
58531
- }
58532
- } else if (input === "c") {
58533
- if (widgets.length > 0) {
58534
- setShowClearConfirm(true);
58535
- }
58536
- } else if (input === " " && widgets.length > 0) {
58537
- const currentWidget2 = widgets[selectedIndex];
58538
- if (currentWidget2 && currentWidget2.type === "separator") {
58539
- const currentChar = currentWidget2.character ?? "|";
58540
- const currentCharIndex = separatorChars.indexOf(currentChar);
58541
- const nextChar = separatorChars[(currentCharIndex + 1) % separatorChars.length];
58542
- const newWidgets = [...widgets];
58543
- newWidgets[selectedIndex] = { ...currentWidget2, character: nextChar };
58544
- onUpdate(newWidgets);
58545
- }
58546
- } else if (input === "r" && widgets.length > 0) {
58547
- const currentWidget2 = widgets[selectedIndex];
58548
- if (currentWidget2 && currentWidget2.type !== "separator" && currentWidget2.type !== "flex-separator") {
58549
- const widgetImpl = getWidget(currentWidget2.type);
58550
- if (!widgetImpl?.supportsRawValue()) {
58551
- return;
58552
- }
58553
- const newWidgets = [...widgets];
58554
- newWidgets[selectedIndex] = { ...currentWidget2, rawValue: !currentWidget2.rawValue };
58555
- onUpdate(newWidgets);
58556
- }
58557
- } else if (input === "m" && widgets.length > 0) {
58558
- const currentWidget2 = widgets[selectedIndex];
58559
- if (currentWidget2 && selectedIndex < widgets.length - 1 && currentWidget2.type !== "separator" && currentWidget2.type !== "flex-separator") {
58560
- const newWidgets = [...widgets];
58561
- let nextMergeState;
58562
- if (currentWidget2.merge === undefined) {
58563
- nextMergeState = true;
58564
- } else if (currentWidget2.merge === true) {
58565
- nextMergeState = "no-padding";
58566
- } else {
58567
- nextMergeState = undefined;
58568
- }
58569
- if (nextMergeState === undefined) {
58570
- const { merge: merge2, ...rest } = currentWidget2;
58571
- newWidgets[selectedIndex] = rest;
58572
- } else {
58573
- newWidgets[selectedIndex] = { ...currentWidget2, merge: nextMergeState };
58574
- }
58575
- onUpdate(newWidgets);
58576
- }
58577
- } else if (key.escape) {
58578
- onBack();
58579
- } else if (widgets.length > 0) {
58580
- const currentWidget2 = widgets[selectedIndex];
58581
- if (currentWidget2 && currentWidget2.type !== "separator" && currentWidget2.type !== "flex-separator") {
58582
- const widgetImpl = getWidget(currentWidget2.type);
58583
- if (widgetImpl) {
58584
- if (widgetImpl.getCustomKeybinds) {
58585
- const customKeybinds2 = getVisibleCustomKeybinds(widgetImpl, currentWidget2);
58586
- const matchedKeybind = customKeybinds2.find((kb) => kb.key === input);
58587
- if (matchedKeybind && !key.ctrl) {
58588
- if (widgetImpl.handleEditorAction) {
58589
- const updatedWidget = widgetImpl.handleEditorAction(matchedKeybind.action, currentWidget2);
58590
- if (updatedWidget) {
58591
- const newWidgets = [...widgets];
58592
- newWidgets[selectedIndex] = updatedWidget;
58593
- onUpdate(newWidgets);
58594
- } else if (widgetImpl.renderEditor) {
58595
- setCustomEditorWidget({ widget: currentWidget2, impl: widgetImpl, action: matchedKeybind.action });
58596
- }
58597
- } else if (widgetImpl.renderEditor) {
58598
- setCustomEditorWidget({ widget: currentWidget2, impl: widgetImpl, action: matchedKeybind.action });
58599
- }
58600
- }
58601
- }
58602
- }
58603
- }
58604
- }
58861
+ handleMoveInputMode({
58862
+ key,
58863
+ widgets,
58864
+ selectedIndex,
58865
+ onUpdate,
58866
+ setSelectedIndex,
58867
+ setMoveMode
58868
+ });
58869
+ return;
58605
58870
  }
58871
+ handleNormalInputMode({
58872
+ input,
58873
+ key,
58874
+ widgets,
58875
+ selectedIndex,
58876
+ separatorChars,
58877
+ onBack,
58878
+ onUpdate,
58879
+ setSelectedIndex,
58880
+ setMoveMode,
58881
+ setShowClearConfirm,
58882
+ openWidgetPicker,
58883
+ getVisibleCustomKeybinds,
58884
+ setCustomEditorWidget
58885
+ });
58606
58886
  });
58607
58887
  const getWidgetDisplay = (widget) => {
58608
58888
  if (widget.type === "separator") {
@@ -58622,7 +58902,7 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
58622
58902
  };
58623
58903
  const hasFlexSeparator = widgets.some((widget) => widget.type === "flex-separator");
58624
58904
  const widthDetectionAvailable = canDetectTerminalWidth();
58625
- const pickerCategories = widgetPicker ? getFilteredCategories(widgetPicker.categoryQuery) : [];
58905
+ const pickerCategories = widgetPicker ? [...widgetCategories] : [];
58626
58906
  const selectedPickerCategory = widgetPicker ? widgetPicker.selectedCategory && pickerCategories.includes(widgetPicker.selectedCategory) ? widgetPicker.selectedCategory : pickerCategories[0] ?? null : null;
58627
58907
  const topLevelSearchEntries = widgetPicker && widgetPicker.level === "category" && widgetPicker.categoryQuery.trim().length > 0 ? filterWidgetCatalog(widgetCatalog, "All", widgetPicker.categoryQuery) : [];
58628
58908
  const selectedTopLevelSearchEntry = widgetPicker ? topLevelSearchEntries.find((entry) => entry.type === widgetPicker.selectedType) ?? topLevelSearchEntries[0] : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline",
3
- "version": "2.1.5",
3
+ "version": "2.1.7",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",