@bastra-recall/statusline 1.0.0 → 1.0.1
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/dist/.build-revision +1 -2
- package/dist/browser.d.ts +100 -102
- package/dist/browser.js +3 -3
- package/dist/index.mjs +14 -14
- package/package.json +3 -3
- package/src/config/defaults.ts +5 -1
- package/src/powerline-layout.ts +174 -0
- package/src/powerline-segments.ts +374 -0
- package/src/powerline-theme.ts +323 -0
- package/src/powerline.ts +53 -835
- package/src/segments/git.ts +3 -3
- package/src/segments/pricing.ts +78 -1
- package/src/segments/renderer-bars.ts +83 -0
- package/src/segments/renderer-format.ts +89 -0
- package/src/segments/renderer-types.ts +171 -0
- package/src/segments/renderer.ts +80 -328
- package/src/segments/session.ts +29 -5
- package/src/segments/today.ts +11 -13
- package/src/tui/sections-bars.ts +215 -0
- package/src/tui/sections-parts.ts +604 -0
- package/src/tui/sections-shared.ts +23 -0
- package/src/tui/sections.ts +67 -819
- package/src/utils/budget.ts +1 -1
- package/src/utils/claude.ts +55 -16
- package/src/utils/formatters.ts +12 -5
- package/src/utils/terminal.ts +5 -5
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import type { PowerlineColors, ColorTheme } from "./themes";
|
|
2
|
+
import type { PowerlineConfig } from "./config/loader";
|
|
3
|
+
import type { PowerlineSymbols } from "./segments";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
hexToAnsi,
|
|
7
|
+
hexToBasicAnsi,
|
|
8
|
+
hexTo256Ansi,
|
|
9
|
+
hexColorDistance,
|
|
10
|
+
} from "./utils/colors";
|
|
11
|
+
import { getColorSupport } from "./utils/color-support";
|
|
12
|
+
import { getTheme } from "./themes";
|
|
13
|
+
import { SYMBOLS, TEXT_SYMBOLS, RESET_CODE } from "./utils/constants";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Glyph and colour theming for PowerlineRenderer (#1039): the separator and
|
|
17
|
+
* icon set for the configured style/charset, the theme resolved to ANSI
|
|
18
|
+
* escape codes for the detected colour depth, and the per-segment-type
|
|
19
|
+
* colour lookups.
|
|
20
|
+
*/
|
|
21
|
+
export class PowerlineTheme {
|
|
22
|
+
constructor(private readonly config: PowerlineConfig) {}
|
|
23
|
+
|
|
24
|
+
initializeSymbols(): PowerlineSymbols {
|
|
25
|
+
const style = this.config.display.style;
|
|
26
|
+
const charset = this.config.display.charset || "unicode";
|
|
27
|
+
const isMinimalStyle = style === "minimal";
|
|
28
|
+
const isCapsuleStyle = style === "capsule";
|
|
29
|
+
const symbolSet = charset === "text" ? TEXT_SYMBOLS : SYMBOLS;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
right: isMinimalStyle
|
|
33
|
+
? ""
|
|
34
|
+
: isCapsuleStyle
|
|
35
|
+
? symbolSet.right_rounded
|
|
36
|
+
: symbolSet.right,
|
|
37
|
+
left: isCapsuleStyle ? symbolSet.left_rounded : "",
|
|
38
|
+
branch: symbolSet.branch,
|
|
39
|
+
model: symbolSet.model,
|
|
40
|
+
git_clean: symbolSet.git_clean,
|
|
41
|
+
git_dirty: symbolSet.git_dirty,
|
|
42
|
+
git_conflicts: symbolSet.git_conflicts,
|
|
43
|
+
git_ahead: symbolSet.git_ahead,
|
|
44
|
+
git_behind: symbolSet.git_behind,
|
|
45
|
+
git_worktree: symbolSet.git_worktree,
|
|
46
|
+
git_tag: symbolSet.git_tag,
|
|
47
|
+
git_sha: symbolSet.git_sha,
|
|
48
|
+
git_upstream: symbolSet.git_upstream,
|
|
49
|
+
git_stash: symbolSet.git_stash,
|
|
50
|
+
git_time: symbolSet.git_time,
|
|
51
|
+
session_cost: symbolSet.session_cost,
|
|
52
|
+
block_cost: symbolSet.block_cost,
|
|
53
|
+
today_cost: symbolSet.today_cost,
|
|
54
|
+
context_time: symbolSet.context_time,
|
|
55
|
+
metrics_response: symbolSet.metrics_response,
|
|
56
|
+
metrics_last_response: symbolSet.metrics_last_response,
|
|
57
|
+
metrics_duration: symbolSet.metrics_duration,
|
|
58
|
+
metrics_messages: symbolSet.metrics_messages,
|
|
59
|
+
metrics_lines_added: symbolSet.metrics_lines_added,
|
|
60
|
+
metrics_lines_removed: symbolSet.metrics_lines_removed,
|
|
61
|
+
metrics_burn: symbolSet.metrics_burn,
|
|
62
|
+
version: symbolSet.version,
|
|
63
|
+
bar_filled: symbolSet.bar_filled,
|
|
64
|
+
bar_empty: symbolSet.bar_empty,
|
|
65
|
+
env: symbolSet.env,
|
|
66
|
+
session_id: symbolSet.session_id,
|
|
67
|
+
weekly_cost: symbolSet.weekly_cost,
|
|
68
|
+
agent: symbolSet.agent,
|
|
69
|
+
thinking: symbolSet.thinking,
|
|
70
|
+
cache_timer: symbolSet.cache_timer,
|
|
71
|
+
bastra: symbolSet.bastra,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getThemeColors(): PowerlineColors {
|
|
76
|
+
const theme = this.config.theme;
|
|
77
|
+
let colorTheme;
|
|
78
|
+
|
|
79
|
+
const colorMode = this.config.display.colorCompatibility || "auto";
|
|
80
|
+
const colorSupport = colorMode === "auto" ? getColorSupport() : colorMode;
|
|
81
|
+
|
|
82
|
+
if (theme === "custom") {
|
|
83
|
+
colorTheme = this.config.colors?.custom;
|
|
84
|
+
if (!colorTheme) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
"Custom theme selected but no colors provided in configuration",
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
colorTheme = getTheme(theme, colorSupport);
|
|
91
|
+
if (!colorTheme) {
|
|
92
|
+
console.warn(
|
|
93
|
+
`Built-in theme '${theme}' not found, falling back to 'dark' theme`,
|
|
94
|
+
);
|
|
95
|
+
colorTheme = getTheme("dark", colorSupport)!;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const convertHex = (hex: string, isBg: boolean): string => {
|
|
100
|
+
if (colorSupport === "none") return "";
|
|
101
|
+
if (colorSupport === "ansi") return hexToBasicAnsi(hex, isBg);
|
|
102
|
+
if (colorSupport === "ansi256") return hexTo256Ansi(hex, isBg);
|
|
103
|
+
return hexToAnsi(hex, isBg);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const fallbackTheme = getTheme("dark", colorSupport)!;
|
|
107
|
+
|
|
108
|
+
const isTui = this.config.display.style === "tui";
|
|
109
|
+
const isLightTheme = theme === "light";
|
|
110
|
+
const terminalRef = isLightTheme ? "#f0f0f0" : "#1e1e1e";
|
|
111
|
+
|
|
112
|
+
const getSegmentColors = (segment: Exclude<keyof ColorTheme, "tui">) => {
|
|
113
|
+
const fallback = fallbackTheme[segment];
|
|
114
|
+
const custom = colorTheme[segment];
|
|
115
|
+
const colors = {
|
|
116
|
+
fg: custom?.fg || fallback.fg,
|
|
117
|
+
bg: custom?.bg || fallback.bg,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
let fgHex = colors.fg;
|
|
121
|
+
if (isTui && hexColorDistance(fgHex, terminalRef) < 60) {
|
|
122
|
+
fgHex = colors.bg;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const bold =
|
|
126
|
+
colorSupport !== "none" && Boolean(custom?.bold ?? fallback.bold);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
bg: convertHex(colors.bg, true),
|
|
130
|
+
fg: convertHex(fgHex, false),
|
|
131
|
+
bold,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const directory = getSegmentColors("directory");
|
|
136
|
+
const git = getSegmentColors("git");
|
|
137
|
+
const model = getSegmentColors("model");
|
|
138
|
+
const session = getSegmentColors("session");
|
|
139
|
+
const block = getSegmentColors("block");
|
|
140
|
+
const today = getSegmentColors("today");
|
|
141
|
+
const tmux = getSegmentColors("tmux");
|
|
142
|
+
const context = getSegmentColors("context");
|
|
143
|
+
const contextWarning = getSegmentColors("contextWarning");
|
|
144
|
+
const contextCritical = getSegmentColors("contextCritical");
|
|
145
|
+
const metrics = getSegmentColors("metrics");
|
|
146
|
+
const version = getSegmentColors("version");
|
|
147
|
+
const env = getSegmentColors("env");
|
|
148
|
+
const weekly = getSegmentColors("weekly");
|
|
149
|
+
const agent = getSegmentColors("agent");
|
|
150
|
+
const thinking = getSegmentColors("thinking");
|
|
151
|
+
const cacheTimer = getSegmentColors("cacheTimer");
|
|
152
|
+
|
|
153
|
+
// bastra brand color — fixed across all themes (not theme-derived), so
|
|
154
|
+
// the bastra segment is always recognizable. White fg on bastra purple.
|
|
155
|
+
const bastra = {
|
|
156
|
+
bg: convertHex("#7c3aed", true),
|
|
157
|
+
fg: convertHex("#ffffff", false),
|
|
158
|
+
bold: colorSupport !== "none",
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
reset: colorSupport === "none" ? "" : RESET_CODE,
|
|
163
|
+
modeBg: directory.bg,
|
|
164
|
+
modeFg: directory.fg,
|
|
165
|
+
modeBold: directory.bold,
|
|
166
|
+
gitBg: git.bg,
|
|
167
|
+
gitFg: git.fg,
|
|
168
|
+
gitBold: git.bold,
|
|
169
|
+
modelBg: model.bg,
|
|
170
|
+
modelFg: model.fg,
|
|
171
|
+
modelBold: model.bold,
|
|
172
|
+
sessionBg: session.bg,
|
|
173
|
+
sessionFg: session.fg,
|
|
174
|
+
sessionBold: session.bold,
|
|
175
|
+
blockBg: block.bg,
|
|
176
|
+
blockFg: block.fg,
|
|
177
|
+
blockBold: block.bold,
|
|
178
|
+
todayBg: today.bg,
|
|
179
|
+
todayFg: today.fg,
|
|
180
|
+
todayBold: today.bold,
|
|
181
|
+
tmuxBg: tmux.bg,
|
|
182
|
+
tmuxFg: tmux.fg,
|
|
183
|
+
tmuxBold: tmux.bold,
|
|
184
|
+
contextBg: context.bg,
|
|
185
|
+
contextFg: context.fg,
|
|
186
|
+
contextBold: context.bold,
|
|
187
|
+
contextWarningBg: contextWarning.bg,
|
|
188
|
+
contextWarningFg: contextWarning.fg,
|
|
189
|
+
contextWarningBold: contextWarning.bold,
|
|
190
|
+
contextCriticalBg: contextCritical.bg,
|
|
191
|
+
contextCriticalFg: contextCritical.fg,
|
|
192
|
+
contextCriticalBold: contextCritical.bold,
|
|
193
|
+
metricsBg: metrics.bg,
|
|
194
|
+
metricsFg: metrics.fg,
|
|
195
|
+
metricsBold: metrics.bold,
|
|
196
|
+
versionBg: version.bg,
|
|
197
|
+
versionFg: version.fg,
|
|
198
|
+
versionBold: version.bold,
|
|
199
|
+
envBg: env.bg,
|
|
200
|
+
envFg: env.fg,
|
|
201
|
+
envBold: env.bold,
|
|
202
|
+
weeklyBg: weekly.bg,
|
|
203
|
+
weeklyFg: weekly.fg,
|
|
204
|
+
weeklyBold: weekly.bold,
|
|
205
|
+
agentBg: agent.bg,
|
|
206
|
+
agentFg: agent.fg,
|
|
207
|
+
agentBold: agent.bold,
|
|
208
|
+
thinkingBg: thinking.bg,
|
|
209
|
+
thinkingFg: thinking.fg,
|
|
210
|
+
thinkingBold: thinking.bold,
|
|
211
|
+
cacheTimerBg: cacheTimer.bg,
|
|
212
|
+
cacheTimerFg: cacheTimer.fg,
|
|
213
|
+
cacheTimerBold: cacheTimer.bold,
|
|
214
|
+
bastraBg: bastra.bg,
|
|
215
|
+
bastraFg: bastra.fg,
|
|
216
|
+
bastraBold: bastra.bold,
|
|
217
|
+
partFg: theme === "custom" ? this.resolvePartColors(convertHex) : {},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private resolvePartColors(
|
|
222
|
+
convertHex: (hex: string, isBg: boolean) => string,
|
|
223
|
+
): Record<string, string> {
|
|
224
|
+
const custom = this.config.colors?.custom as
|
|
225
|
+
| Record<string, { fg?: string }>
|
|
226
|
+
| undefined;
|
|
227
|
+
if (!custom) return {};
|
|
228
|
+
|
|
229
|
+
const result: Record<string, string> = {};
|
|
230
|
+
for (const key of Object.keys(custom)) {
|
|
231
|
+
const entry = custom[key];
|
|
232
|
+
if (!entry?.fg) continue;
|
|
233
|
+
result[key] = convertHex(entry.fg, false);
|
|
234
|
+
}
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private getSegmentBgColor(
|
|
239
|
+
segmentType: string,
|
|
240
|
+
colors: PowerlineColors,
|
|
241
|
+
): string {
|
|
242
|
+
switch (segmentType) {
|
|
243
|
+
case "directory":
|
|
244
|
+
return colors.modeBg;
|
|
245
|
+
case "git":
|
|
246
|
+
return colors.gitBg;
|
|
247
|
+
case "model":
|
|
248
|
+
return colors.modelBg;
|
|
249
|
+
case "session":
|
|
250
|
+
case "sessionId":
|
|
251
|
+
return colors.sessionBg;
|
|
252
|
+
case "block":
|
|
253
|
+
return colors.blockBg;
|
|
254
|
+
case "today":
|
|
255
|
+
return colors.todayBg;
|
|
256
|
+
case "tmux":
|
|
257
|
+
return colors.tmuxBg;
|
|
258
|
+
case "context":
|
|
259
|
+
return colors.contextBg;
|
|
260
|
+
case "metrics":
|
|
261
|
+
return colors.metricsBg;
|
|
262
|
+
case "version":
|
|
263
|
+
return colors.versionBg;
|
|
264
|
+
case "env":
|
|
265
|
+
return colors.envBg;
|
|
266
|
+
case "weekly":
|
|
267
|
+
return colors.weeklyBg;
|
|
268
|
+
case "agent":
|
|
269
|
+
return colors.agentBg;
|
|
270
|
+
case "thinking":
|
|
271
|
+
return colors.thinkingBg;
|
|
272
|
+
case "cacheTimer":
|
|
273
|
+
return colors.cacheTimerBg;
|
|
274
|
+
case "bastra":
|
|
275
|
+
return colors.bastraBg;
|
|
276
|
+
default:
|
|
277
|
+
return colors.modeBg;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
getSegmentBoldFlag(
|
|
282
|
+
segmentType: string,
|
|
283
|
+
colors: PowerlineColors,
|
|
284
|
+
): boolean {
|
|
285
|
+
switch (segmentType) {
|
|
286
|
+
case "directory":
|
|
287
|
+
return colors.modeBold;
|
|
288
|
+
case "git":
|
|
289
|
+
return colors.gitBold;
|
|
290
|
+
case "model":
|
|
291
|
+
return colors.modelBold;
|
|
292
|
+
case "session":
|
|
293
|
+
case "sessionId":
|
|
294
|
+
return colors.sessionBold;
|
|
295
|
+
case "block":
|
|
296
|
+
return colors.blockBold;
|
|
297
|
+
case "today":
|
|
298
|
+
return colors.todayBold;
|
|
299
|
+
case "tmux":
|
|
300
|
+
return colors.tmuxBold;
|
|
301
|
+
case "context":
|
|
302
|
+
return colors.contextBold;
|
|
303
|
+
case "metrics":
|
|
304
|
+
return colors.metricsBold;
|
|
305
|
+
case "version":
|
|
306
|
+
return colors.versionBold;
|
|
307
|
+
case "env":
|
|
308
|
+
return colors.envBold;
|
|
309
|
+
case "weekly":
|
|
310
|
+
return colors.weeklyBold;
|
|
311
|
+
case "agent":
|
|
312
|
+
return colors.agentBold;
|
|
313
|
+
case "thinking":
|
|
314
|
+
return colors.thinkingBold;
|
|
315
|
+
case "cacheTimer":
|
|
316
|
+
return colors.cacheTimerBold;
|
|
317
|
+
case "bastra":
|
|
318
|
+
return colors.bastraBold;
|
|
319
|
+
default:
|
|
320
|
+
return colors.modeBold;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|