claude-contextline 2.1.0 → 2.3.0

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.
Files changed (3) hide show
  1. package/README.md +6 -9
  2. package/dist/index.js +47 -156
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,15 +23,12 @@ Add to your Claude Code settings (`~/.claude/settings.json`):
23
23
  (main) myproject
24
24
  ```
25
25
 
26
- Colors track your active [herdr](https://github.com/) theme, read from
27
- `~/.config/herdr/config.toml` (respects `$XDG_CONFIG_HOME` and
28
- `$HERDR_CONFIG_PATH`) on every render including `[theme.custom]` overrides.
29
- All of herdr's built-in themes are supported (catppuccin, tokyo-night, dracula,
30
- nord, gruvbox, solarized, kanagawa, rosé-pine, vesper, ); if no config is found
31
- it falls back to Catppuccin Mocha.
32
-
33
- - **Line 1**: Context window battery bar (blue) with usage percentage, model name (red)
34
- - **Line 2**: Git branch (mauve), working directory (blue) aligned below the model
26
+ Colors reflect [lasso](https://github.com/)'s Onyx design system — lasso's
27
+ single, fixed (dark) palette — so the statusline stays in step with lasso's
28
+ indigo-forward look. The tokens mirror lasso's canonical Onyx colors.
29
+
30
+ - **Line 1**: Context window battery bar (Onyx accent) with usage percentage, model name (Onyx danger)
31
+ - **Line 2**: Git branch (Onyx secondary indigo), working directory (Onyx accent) aligned below the model
35
32
 
36
33
  ## Requirements
37
34
 
package/dist/index.js CHANGED
@@ -89,162 +89,52 @@ function getUsedPercentage(hookData) {
89
89
  import { readFileSync } from "fs";
90
90
  import { homedir } from "os";
91
91
  import { join } from "path";
92
- var rgb = (r, g, b) => ({ kind: "rgb", r, g, b });
93
- var ansi = (code) => ({ kind: "ansi", code });
94
- var RESET_COLOR = { kind: "reset" };
95
- var PALETTES = {
96
- catppuccin: { blue: rgb(137, 180, 250), red: rgb(243, 139, 168), mauve: rgb(203, 166, 247), overlay0: rgb(108, 112, 134) },
97
- "catppuccin-latte": { blue: rgb(30, 102, 245), red: rgb(210, 15, 57), mauve: rgb(136, 57, 239), overlay0: rgb(156, 160, 176) },
98
- terminal: { blue: ansi(34), red: ansi(91), mauve: ansi(37), overlay0: ansi(37) },
99
- "tokyo-night": { blue: rgb(122, 162, 247), red: rgb(247, 118, 142), mauve: rgb(187, 154, 247), overlay0: rgb(86, 95, 137) },
100
- "tokyo-night-day": { blue: rgb(46, 125, 233), red: rgb(245, 42, 101), mauve: rgb(120, 71, 189), overlay0: rgb(137, 144, 179) },
101
- dracula: { blue: rgb(139, 233, 253), red: rgb(255, 85, 85), mauve: rgb(255, 121, 198), overlay0: rgb(98, 114, 164) },
102
- nord: { blue: rgb(129, 161, 193), red: rgb(191, 97, 106), mauve: rgb(180, 142, 173), overlay0: rgb(76, 86, 106) },
103
- gruvbox: { blue: rgb(131, 165, 152), red: rgb(251, 73, 52), mauve: rgb(211, 134, 155), overlay0: rgb(146, 131, 116) },
104
- "gruvbox-light": { blue: rgb(7, 102, 120), red: rgb(157, 0, 6), mauve: rgb(143, 63, 113), overlay0: rgb(146, 131, 116) },
105
- "one-dark": { blue: rgb(97, 175, 239), red: rgb(224, 108, 117), mauve: rgb(198, 120, 221), overlay0: rgb(92, 99, 112) },
106
- "one-light": { blue: rgb(64, 120, 242), red: rgb(228, 86, 73), mauve: rgb(166, 38, 164), overlay0: rgb(160, 161, 167) },
107
- solarized: { blue: rgb(38, 139, 210), red: rgb(220, 50, 47), mauve: rgb(211, 54, 130), overlay0: rgb(88, 110, 117) },
108
- "solarized-light": { blue: rgb(38, 139, 210), red: rgb(220, 50, 47), mauve: rgb(211, 54, 130), overlay0: rgb(147, 161, 161) },
109
- kanagawa: { blue: rgb(126, 156, 216), red: rgb(195, 64, 67), mauve: rgb(149, 127, 184), overlay0: rgb(114, 113, 105) },
110
- "kanagawa-lotus": { blue: rgb(77, 105, 155), red: rgb(200, 64, 83), mauve: rgb(98, 76, 131), overlay0: rgb(160, 156, 172) },
111
- "rose-pine": { blue: rgb(49, 116, 143), red: rgb(235, 111, 146), mauve: rgb(196, 167, 231), overlay0: rgb(110, 106, 134) },
112
- "rose-pine-dawn": { blue: rgb(40, 105, 131), red: rgb(180, 99, 122), mauve: rgb(144, 122, 169), overlay0: rgb(152, 147, 165) },
113
- vesper: { blue: rgb(176, 176, 176), red: rgb(255, 128, 128), mauve: rgb(255, 209, 168), overlay0: rgb(92, 92, 92) }
114
- };
115
- var ALIASES = {
116
- "catppuccin-mocha": "catppuccin",
117
- latte: "catppuccin-latte",
118
- light: "catppuccin-latte",
119
- tokyonight: "tokyo-night",
120
- "tokyo-day": "tokyo-night-day",
121
- "tokyonight-day": "tokyo-night-day",
122
- "gruvbox-dark": "gruvbox",
123
- onedark: "one-dark",
124
- onelight: "one-light",
125
- "solarized-dark": "solarized",
126
- lotus: "kanagawa-lotus",
127
- rosepine: "rose-pine",
128
- "rosepine-dawn": "rose-pine-dawn",
129
- dawn: "rose-pine-dawn"
130
- };
131
- var DEFAULT_THEME = "catppuccin";
132
- var NAMED_ANSI = {
133
- black: 30,
134
- red: 31,
135
- green: 32,
136
- yellow: 33,
137
- blue: 34,
138
- magenta: 35,
139
- purple: 35,
140
- cyan: 36,
141
- gray: 37,
142
- grey: 37,
143
- white: 97,
144
- darkgray: 90,
145
- darkgrey: 90,
146
- lightred: 91,
147
- lightgreen: 92,
148
- lightyellow: 93,
149
- lightblue: 94,
150
- lightmagenta: 95,
151
- lightcyan: 96
152
- };
153
- function fg(color) {
154
- switch (color.kind) {
155
- case "rgb":
156
- return `\x1B[38;2;${color.r};${color.g};${color.b}m`;
157
- case "ansi":
158
- return `\x1B[${color.code}m`;
159
- case "reset":
160
- return `\x1B[39m`;
161
- }
92
+ function fg(hex) {
93
+ const r = parseInt(hex.slice(1, 3), 16);
94
+ const g = parseInt(hex.slice(3, 5), 16);
95
+ const b = parseInt(hex.slice(5, 7), 16);
96
+ return `\x1B[38;2;${r};${g};${b}m`;
162
97
  }
163
- function parseColor(input) {
164
- const s = input.trim().toLowerCase();
165
- if (s === "reset" || s === "default" || s === "none" || s === "transparent") {
166
- return RESET_COLOR;
98
+ var NOTHING = {
99
+ dark: {
100
+ bar: fg("#ededed"),
101
+ // --h-fg (filled bar + percentage)
102
+ barFull: fg("#d71921"),
103
+ // --h-bad (context ≥90% — interrupt)
104
+ text: fg("#ededed"),
105
+ // --h-fg (model + directory)
106
+ branch: fg("#8a8a8a"),
107
+ // --h-muted (git branch)
108
+ empty: fg("#3a3a3a")
109
+ // --h-border+ (empty bar cells)
110
+ },
111
+ light: {
112
+ bar: fg("#1a1a1a"),
113
+ // --h-fg (light)
114
+ barFull: fg("#c0141b"),
115
+ // --h-bad (light, darkened)
116
+ text: fg("#1a1a1a"),
117
+ // --h-fg (light)
118
+ branch: fg("#666666"),
119
+ // --h-muted (light)
120
+ empty: fg("#cccccc")
121
+ // light seam
167
122
  }
168
- if (s.startsWith("#")) {
169
- const hex = s.slice(1);
170
- if (/^[0-9a-f]{6}$/.test(hex)) {
171
- return rgb(parseInt(hex.slice(0, 2), 16), parseInt(hex.slice(2, 4), 16), parseInt(hex.slice(4, 6), 16));
172
- }
173
- if (/^[0-9a-f]{3}$/.test(hex)) {
174
- return rgb(parseInt(hex[0], 16) * 17, parseInt(hex[1], 16) * 17, parseInt(hex[2], 16) * 17);
175
- }
176
- }
177
- const rgbMatch = s.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
178
- if (rgbMatch) {
179
- return rgb(Number(rgbMatch[1]), Number(rgbMatch[2]), Number(rgbMatch[3]));
180
- }
181
- if (s in NAMED_ANSI) {
182
- return ansi(NAMED_ANSI[s]);
183
- }
184
- return ansi(36);
185
- }
186
- function configPaths() {
187
- const override = process.env.HERDR_CONFIG_PATH;
188
- if (override) return [override];
189
- const base = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
190
- return [join(base, "herdr", "config.toml"), join(base, "herdr-dev", "config.toml")];
123
+ };
124
+ function settingsPath() {
125
+ const dir = process.env.LASSO_DIR || join(homedir(), ".lasso");
126
+ return join(dir, "settings.json");
191
127
  }
192
- function parseThemeSection(toml) {
193
- const result = { custom: {} };
194
- let section = "";
195
- for (const rawLine of toml.split(/\r?\n/)) {
196
- const line = rawLine.trim();
197
- if (!line || line.startsWith("#")) continue;
198
- const sectionMatch = line.match(/^\[([^\]]+)\]$/);
199
- if (sectionMatch) {
200
- section = sectionMatch[1].trim();
201
- continue;
202
- }
203
- const eq = line.indexOf("=");
204
- if (eq === -1) continue;
205
- const key = line.slice(0, eq).trim();
206
- let value = line.slice(eq + 1).trim();
207
- if (value.startsWith('"') || value.startsWith("'")) {
208
- const quote = value[0];
209
- const end = value.indexOf(quote, 1);
210
- if (end !== -1) value = value.slice(1, end);
211
- } else {
212
- const hash = value.indexOf("#");
213
- if (hash !== -1) value = value.slice(0, hash).trim();
214
- }
215
- if (section === "theme" && key === "name") {
216
- result.name = value;
217
- } else if (section === "theme.custom") {
218
- result.custom[key] = value;
219
- }
128
+ function resolvedAppearance() {
129
+ try {
130
+ const parsed = JSON.parse(readFileSync(settingsPath(), "utf8"));
131
+ return parsed?.theme?.resolved === "light" ? "light" : "dark";
132
+ } catch {
133
+ return "dark";
220
134
  }
221
- return result;
222
- }
223
- function resolvePalette(name) {
224
- const normalized = (name ?? DEFAULT_THEME).toLowerCase().replace(/[ _]/g, "-");
225
- const canonical = ALIASES[normalized] ?? normalized;
226
- return PALETTES[canonical] ?? PALETTES[DEFAULT_THEME];
227
135
  }
228
- function loadHerdrTheme() {
229
- let parsed = { custom: {} };
230
- for (const path of configPaths()) {
231
- try {
232
- parsed = parseThemeSection(readFileSync(path, "utf8"));
233
- break;
234
- } catch {
235
- }
236
- }
237
- const palette = { ...resolvePalette(parsed.name) };
238
- for (const token of ["blue", "red", "mauve", "overlay0"]) {
239
- const override = parsed.custom[token];
240
- if (override) palette[token] = parseColor(override);
241
- }
242
- return {
243
- blue: fg(palette.blue),
244
- red: fg(palette.red),
245
- mauve: fg(palette.mauve),
246
- overlay0: fg(palette.overlay0)
247
- };
136
+ function loadLassoTheme() {
137
+ return NOTHING[resolvedAppearance()];
248
138
  }
249
139
 
250
140
  // src/renderer.ts
@@ -253,7 +143,7 @@ var BAR_WIDTH = 10;
253
143
  var FILLED_CHAR = "\u2588";
254
144
  var EMPTY_CHAR = "\u2591";
255
145
  function render(envInfo) {
256
- const { blue: BLUE, red: RED, mauve: MAUVE, overlay0: GRAY } = loadHerdrTheme();
146
+ const { bar: BAR, barFull: BAR_FULL, text: TEXT, branch: BRANCH, empty: EMPTY } = loadLassoTheme();
257
147
  let out = "";
258
148
  let modelCol = 0;
259
149
  if (envInfo.usedPercentage != null) {
@@ -266,23 +156,24 @@ function render(envInfo) {
266
156
  const nEmpty = BAR_WIDTH - nFilled;
267
157
  const filled = FILLED_CHAR.repeat(nFilled);
268
158
  const empty = EMPTY_CHAR.repeat(nEmpty);
269
- out += `${BLUE}[${filled}${GRAY}${empty}${BLUE}] ${pctStr}%`;
159
+ const fill = pct >= 90 ? BAR_FULL : BAR;
160
+ out += `${fill}[${filled}${EMPTY}${empty}${fill}] ${pctStr}%`;
270
161
  modelCol = 16 + pctStr.length;
271
162
  }
272
163
  if (out.length > 0) {
273
164
  out += " ";
274
165
  }
275
- out += `${RED}${envInfo.model}`;
166
+ out += `${TEXT}${envInfo.model}`;
276
167
  out += "\n";
277
168
  if (envInfo.gitBranch) {
278
169
  const branchText = `(${envInfo.gitBranch})`;
279
- out += `${MAUVE}${branchText}`;
170
+ out += `${BRANCH}${branchText}`;
280
171
  const gap = Math.max(2, modelCol - branchText.length);
281
172
  out += " ".repeat(gap);
282
173
  } else {
283
174
  out += " ".repeat(modelCol);
284
175
  }
285
- out += `${BLUE}${envInfo.directory}`;
176
+ out += `${TEXT}${envInfo.directory}`;
286
177
  out += RESET;
287
178
  return out;
288
179
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-contextline",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "Two-line statusline for Claude Code showing context window usage",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",