@v1nvn/statusline 0.28.0 → 0.29.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.
- package/README.md +69 -47
- package/dist/index.js +1553 -397
- package/dist/index.js.map +1 -1
- package/dist/render.mjs +1452 -0
- package/dist/render.mjs.map +1 -0
- package/package.json +3 -3
package/dist/render.mjs
ADDED
|
@@ -0,0 +1,1452 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
//#region src/render/items.ts
|
|
7
|
+
var ITEMS = [
|
|
8
|
+
{
|
|
9
|
+
alternatives: [
|
|
10
|
+
"plain",
|
|
11
|
+
"block",
|
|
12
|
+
"pill",
|
|
13
|
+
"zen"
|
|
14
|
+
],
|
|
15
|
+
default: "plain",
|
|
16
|
+
item: "model"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
alternatives: [
|
|
20
|
+
"plain",
|
|
21
|
+
"dim",
|
|
22
|
+
"hidden"
|
|
23
|
+
],
|
|
24
|
+
default: "plain",
|
|
25
|
+
item: "effort"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
alternatives: ["none", "pills"],
|
|
29
|
+
default: "none",
|
|
30
|
+
item: "state"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
alternatives: [
|
|
34
|
+
"init",
|
|
35
|
+
"full",
|
|
36
|
+
"tail",
|
|
37
|
+
"base",
|
|
38
|
+
"icon"
|
|
39
|
+
],
|
|
40
|
+
default: "init",
|
|
41
|
+
item: "cwd"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
alternatives: [
|
|
45
|
+
"initials",
|
|
46
|
+
"full",
|
|
47
|
+
"last",
|
|
48
|
+
"icon",
|
|
49
|
+
"none"
|
|
50
|
+
],
|
|
51
|
+
default: "initials",
|
|
52
|
+
item: "branch"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
alternatives: [
|
|
56
|
+
"counts",
|
|
57
|
+
"icons",
|
|
58
|
+
"none"
|
|
59
|
+
],
|
|
60
|
+
default: "counts",
|
|
61
|
+
item: "status"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
alternatives: ["none", "arrows"],
|
|
65
|
+
default: "none",
|
|
66
|
+
item: "ahead"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
alternatives: ["none", "badge"],
|
|
70
|
+
default: "none",
|
|
71
|
+
item: "pr"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
alternatives: [
|
|
75
|
+
"flat",
|
|
76
|
+
"gauge",
|
|
77
|
+
"percent",
|
|
78
|
+
"none",
|
|
79
|
+
"flat6",
|
|
80
|
+
"flat4"
|
|
81
|
+
],
|
|
82
|
+
default: "flat",
|
|
83
|
+
item: "bar"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
alternatives: [
|
|
87
|
+
"full",
|
|
88
|
+
"compact",
|
|
89
|
+
"free",
|
|
90
|
+
"none"
|
|
91
|
+
],
|
|
92
|
+
default: "full",
|
|
93
|
+
item: "tokens"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
alternatives: [
|
|
97
|
+
"hit",
|
|
98
|
+
"coldin",
|
|
99
|
+
"fuse",
|
|
100
|
+
"none"
|
|
101
|
+
],
|
|
102
|
+
default: "hit",
|
|
103
|
+
item: "cache"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
alternatives: [
|
|
107
|
+
"plain",
|
|
108
|
+
"burn",
|
|
109
|
+
"none"
|
|
110
|
+
],
|
|
111
|
+
default: "plain",
|
|
112
|
+
item: "cost"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
alternatives: [
|
|
116
|
+
"clock",
|
|
117
|
+
"hours",
|
|
118
|
+
"none"
|
|
119
|
+
],
|
|
120
|
+
default: "clock",
|
|
121
|
+
item: "duration"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
alternatives: ["none", "diffstat"],
|
|
125
|
+
default: "none",
|
|
126
|
+
item: "lines"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
alternatives: ["none", "strip"],
|
|
130
|
+
default: "none",
|
|
131
|
+
item: "rate"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
alternatives: [
|
|
135
|
+
"plain",
|
|
136
|
+
"dots",
|
|
137
|
+
"dim",
|
|
138
|
+
"bare"
|
|
139
|
+
],
|
|
140
|
+
default: "plain",
|
|
141
|
+
item: "style"
|
|
142
|
+
}
|
|
143
|
+
];
|
|
144
|
+
var DEFAULT_LAYOUT = "{model effort state} {cwd branch status ahead pr} {bar tokens cache} {cost} {duration} {lines} {rate}";
|
|
145
|
+
var BY_ITEM = new Map(ITEMS.map((spec) => [spec.item, spec]));
|
|
146
|
+
function specFor(item) {
|
|
147
|
+
return BY_ITEM.get(item);
|
|
148
|
+
}
|
|
149
|
+
ITEMS.map((spec) => spec.item);
|
|
150
|
+
Object.fromEntries(ITEMS.map(({ default: alt, item }) => [item, alt]));
|
|
151
|
+
var RUNG_ORDERS = {
|
|
152
|
+
bar: [
|
|
153
|
+
"flat",
|
|
154
|
+
"flat6",
|
|
155
|
+
"flat4",
|
|
156
|
+
"percent",
|
|
157
|
+
"none"
|
|
158
|
+
],
|
|
159
|
+
branch: [
|
|
160
|
+
"icon",
|
|
161
|
+
"full",
|
|
162
|
+
"initials",
|
|
163
|
+
"last",
|
|
164
|
+
"none"
|
|
165
|
+
],
|
|
166
|
+
cache: [
|
|
167
|
+
"hit",
|
|
168
|
+
"coldin",
|
|
169
|
+
"none"
|
|
170
|
+
],
|
|
171
|
+
cwd: [
|
|
172
|
+
"icon",
|
|
173
|
+
"full",
|
|
174
|
+
"init",
|
|
175
|
+
"tail",
|
|
176
|
+
"base"
|
|
177
|
+
],
|
|
178
|
+
duration: [
|
|
179
|
+
"clock",
|
|
180
|
+
"hours",
|
|
181
|
+
"none"
|
|
182
|
+
],
|
|
183
|
+
effort: [
|
|
184
|
+
"plain",
|
|
185
|
+
"dim",
|
|
186
|
+
"hidden"
|
|
187
|
+
],
|
|
188
|
+
status: [
|
|
189
|
+
"counts",
|
|
190
|
+
"icons",
|
|
191
|
+
"none"
|
|
192
|
+
],
|
|
193
|
+
tokens: [
|
|
194
|
+
"full",
|
|
195
|
+
"free",
|
|
196
|
+
"compact",
|
|
197
|
+
"none"
|
|
198
|
+
]
|
|
199
|
+
};
|
|
200
|
+
var FULL_STEPS = [
|
|
201
|
+
["duration", "none"],
|
|
202
|
+
["cache", "none"],
|
|
203
|
+
["tokens", "compact"],
|
|
204
|
+
["bar", "flat6"],
|
|
205
|
+
["status", "none"],
|
|
206
|
+
["branch", "initials"],
|
|
207
|
+
["cwd", "init"],
|
|
208
|
+
["branch", "last"],
|
|
209
|
+
["bar", "flat4"],
|
|
210
|
+
["model", "strip"],
|
|
211
|
+
["bar", "percent"],
|
|
212
|
+
["branch", "none"],
|
|
213
|
+
["cwd", "tail"],
|
|
214
|
+
["effort", "hidden"],
|
|
215
|
+
["cwd", "base"],
|
|
216
|
+
["tokens", "none"]
|
|
217
|
+
];
|
|
218
|
+
var L1_STEPS = [
|
|
219
|
+
["status", "none"],
|
|
220
|
+
["branch", "initials"],
|
|
221
|
+
["cwd", "init"],
|
|
222
|
+
["branch", "last"],
|
|
223
|
+
["model", "strip"],
|
|
224
|
+
["branch", "none"],
|
|
225
|
+
["cwd", "tail"],
|
|
226
|
+
["effort", "hidden"],
|
|
227
|
+
["cwd", "base"]
|
|
228
|
+
];
|
|
229
|
+
var L2_STEPS = [
|
|
230
|
+
["duration", "none"],
|
|
231
|
+
["cache", "none"],
|
|
232
|
+
["tokens", "compact"],
|
|
233
|
+
["bar", "flat6"],
|
|
234
|
+
["bar", "flat4"],
|
|
235
|
+
["tokens", "none"],
|
|
236
|
+
["bar", "percent"]
|
|
237
|
+
];
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/render/argv.ts
|
|
240
|
+
function warn$1(warnings, message) {
|
|
241
|
+
warnings.push(message);
|
|
242
|
+
}
|
|
243
|
+
function parseArgv(argv) {
|
|
244
|
+
const parsed = parseArgs({
|
|
245
|
+
allowPositionals: true,
|
|
246
|
+
args: [...argv],
|
|
247
|
+
options: {
|
|
248
|
+
layout: { type: "string" },
|
|
249
|
+
now: { type: "string" },
|
|
250
|
+
theme: { type: "string" }
|
|
251
|
+
},
|
|
252
|
+
strict: false
|
|
253
|
+
});
|
|
254
|
+
const warnings = [];
|
|
255
|
+
const picks = {};
|
|
256
|
+
let mode = "line";
|
|
257
|
+
let theme;
|
|
258
|
+
let layout;
|
|
259
|
+
let now;
|
|
260
|
+
for (const positional of parsed.positionals) if (positional === "panel") mode = "panel";
|
|
261
|
+
else warn$1(warnings, `statusline: unexpected argument '${positional}', ignored`);
|
|
262
|
+
for (const [name, value] of Object.entries(parsed.values)) {
|
|
263
|
+
if (typeof value !== "string") {
|
|
264
|
+
warn$1(warnings, `statusline: --${name} needs a value, ignored`);
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (name === "theme") {
|
|
268
|
+
theme = value;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (name === "layout") {
|
|
272
|
+
layout = value;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (name === "now") {
|
|
276
|
+
const epoch = Number(value);
|
|
277
|
+
if (Number.isFinite(epoch) && value.trim() !== "") now = epoch;
|
|
278
|
+
else warn$1(warnings, `statusline: --now=${value} is not a number, ignored`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const spec = specFor(name);
|
|
282
|
+
if (spec === void 0) {
|
|
283
|
+
warn$1(warnings, `statusline: --${name} is not a known flag, ignored`);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (spec.alternatives.includes(value)) picks[name] = value;
|
|
287
|
+
else warn$1(warnings, `statusline: ${name}=${value} is not available, using ${name}=${spec.default}`);
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
mode,
|
|
291
|
+
picks,
|
|
292
|
+
warnings,
|
|
293
|
+
...theme === void 0 ? {} : { theme },
|
|
294
|
+
...layout === void 0 ? {} : { layout },
|
|
295
|
+
...now === void 0 ? {} : { now }
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/render/capture.ts
|
|
300
|
+
var DATA_DIR = join(".claude", "plugins", "data", "statusline-agentic");
|
|
301
|
+
function capturePayload(home, surface, payload) {
|
|
302
|
+
const dest = join(home, DATA_DIR, "captures", `${surface}.json`);
|
|
303
|
+
try {
|
|
304
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
305
|
+
writeFileSync(`${dest}.tmp`, payload);
|
|
306
|
+
renameSync(`${dest}.tmp`, dest);
|
|
307
|
+
} catch {}
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/render/git.ts
|
|
311
|
+
var BRANCH_HEAD = "# branch.head ";
|
|
312
|
+
var BRANCH_AB = /^# branch\.ab \+(\d+) -(\d+)$/;
|
|
313
|
+
function gitEnv(env) {
|
|
314
|
+
return {
|
|
315
|
+
HOME: env.home,
|
|
316
|
+
LC_ALL: "C",
|
|
317
|
+
PATH: process.env.PATH ?? ""
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function gitText(dir, args, env) {
|
|
321
|
+
const run = spawnSync("git", [
|
|
322
|
+
"-C",
|
|
323
|
+
dir,
|
|
324
|
+
...args
|
|
325
|
+
], {
|
|
326
|
+
encoding: "utf8",
|
|
327
|
+
env: gitEnv(env)
|
|
328
|
+
});
|
|
329
|
+
return run.status === 0 ? run.stdout : null;
|
|
330
|
+
}
|
|
331
|
+
function lineCount(text) {
|
|
332
|
+
return text === null || text === "" ? 0 : (text.match(/\n/g) ?? []).length;
|
|
333
|
+
}
|
|
334
|
+
function count(text) {
|
|
335
|
+
const parsed = text === null ? NaN : Number(text.trim());
|
|
336
|
+
return Number.isFinite(parsed) ? Math.trunc(parsed) : 0;
|
|
337
|
+
}
|
|
338
|
+
function readGit(dir, env) {
|
|
339
|
+
const facts = {
|
|
340
|
+
ahead: 0,
|
|
341
|
+
behind: 0,
|
|
342
|
+
branch: "",
|
|
343
|
+
modified: 0,
|
|
344
|
+
staged: 0,
|
|
345
|
+
stashes: 0,
|
|
346
|
+
untracked: 0
|
|
347
|
+
};
|
|
348
|
+
if (dir === "") return facts;
|
|
349
|
+
if (spawnSync("git", [
|
|
350
|
+
"-C",
|
|
351
|
+
dir,
|
|
352
|
+
"rev-parse",
|
|
353
|
+
"--git-dir"
|
|
354
|
+
], {
|
|
355
|
+
encoding: "utf8",
|
|
356
|
+
env: gitEnv(env)
|
|
357
|
+
}).status !== 0) return facts;
|
|
358
|
+
const status = gitText(dir, [
|
|
359
|
+
"status",
|
|
360
|
+
"--porcelain=v2",
|
|
361
|
+
"--branch"
|
|
362
|
+
], env);
|
|
363
|
+
for (const line of (status ?? "").split("\n")) {
|
|
364
|
+
if (line.startsWith(BRANCH_HEAD)) {
|
|
365
|
+
facts.branch = line.slice(14);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const ab = BRANCH_AB.exec(line);
|
|
369
|
+
if (ab !== null) {
|
|
370
|
+
facts.ahead = count(ab[1]);
|
|
371
|
+
facts.behind = count(ab[2]);
|
|
372
|
+
} else if ((line.startsWith("1 ") || line.startsWith("2 ")) && line.length >= 4) {
|
|
373
|
+
if (line[2] !== ".") facts.staged += 1;
|
|
374
|
+
if (line[3] !== ".") facts.modified += 1;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (facts.branch === "(detached)") facts.branch = "";
|
|
378
|
+
facts.untracked = lineCount(gitText(dir, [
|
|
379
|
+
"ls-files",
|
|
380
|
+
"--others",
|
|
381
|
+
"--exclude-standard"
|
|
382
|
+
], env));
|
|
383
|
+
facts.stashes = lineCount(gitText(dir, ["stash", "list"], env));
|
|
384
|
+
return facts;
|
|
385
|
+
}
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/render/layout.ts
|
|
388
|
+
function parseClusters(layout) {
|
|
389
|
+
const clusters = [];
|
|
390
|
+
let words = [];
|
|
391
|
+
let word = "";
|
|
392
|
+
let open = false;
|
|
393
|
+
function pushWord() {
|
|
394
|
+
if (word !== "") {
|
|
395
|
+
words.push(word);
|
|
396
|
+
word = "";
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
for (const c of layout) if (c === "{") {
|
|
400
|
+
if (open) throw new Error(`layout '${layout}': '{' inside a cluster`);
|
|
401
|
+
open = true;
|
|
402
|
+
words = [];
|
|
403
|
+
} else if (c === "}") {
|
|
404
|
+
if (!open) throw new Error(`layout '${layout}': '}' outside a cluster`);
|
|
405
|
+
pushWord();
|
|
406
|
+
open = false;
|
|
407
|
+
if (words.length > 0) clusters.push(words);
|
|
408
|
+
} else if (c === " ") {
|
|
409
|
+
if (open) pushWord();
|
|
410
|
+
else if (word !== "") throw new Error(`layout '${layout}': '${word}' sits outside a cluster`);
|
|
411
|
+
} else if (/[a-z0-9]/.test(c)) word += c;
|
|
412
|
+
else throw new Error(`layout '${layout}': '${c}' is not layout grammar (braces, item ids, spaces)`);
|
|
413
|
+
if (open) throw new Error(`layout '${layout}': unterminated cluster`);
|
|
414
|
+
if (word !== "") throw new Error(`layout '${layout}': '${word}' sits outside a cluster`);
|
|
415
|
+
if (clusters.length === 0) throw new Error(`layout '${layout}': no clusters`);
|
|
416
|
+
return clusters;
|
|
417
|
+
}
|
|
418
|
+
//#endregion
|
|
419
|
+
//#region src/render/jq.ts
|
|
420
|
+
function isRecord(value) {
|
|
421
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
422
|
+
}
|
|
423
|
+
function orElse(value, fallback) {
|
|
424
|
+
return value === void 0 || value === null || value === false ? fallback : value;
|
|
425
|
+
}
|
|
426
|
+
function jqText(value) {
|
|
427
|
+
if (typeof value === "string") return value;
|
|
428
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
429
|
+
return JSON.stringify(value);
|
|
430
|
+
}
|
|
431
|
+
function tsvEscape(text) {
|
|
432
|
+
return text.replace(/\\/g, "\\\\").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
433
|
+
}
|
|
434
|
+
//#endregion
|
|
435
|
+
//#region src/render/payload.ts
|
|
436
|
+
function member(source, key) {
|
|
437
|
+
return isRecord(source) ? source[key] : void 0;
|
|
438
|
+
}
|
|
439
|
+
function field$1(source, path) {
|
|
440
|
+
let current = source;
|
|
441
|
+
for (const key of path.split(".")) current = member(current, key);
|
|
442
|
+
return current;
|
|
443
|
+
}
|
|
444
|
+
function textOf(value) {
|
|
445
|
+
if (typeof value === "string") return value;
|
|
446
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
447
|
+
return "";
|
|
448
|
+
}
|
|
449
|
+
function scalar(source, path, fallback) {
|
|
450
|
+
return textOf(orElse(field$1(source, path), fallback));
|
|
451
|
+
}
|
|
452
|
+
function numberField(source, path) {
|
|
453
|
+
return Number(scalar(source, path, "0"));
|
|
454
|
+
}
|
|
455
|
+
function pctField(source) {
|
|
456
|
+
const intPart = scalar(source, "context_window.used_percentage", "0").split(".")[0];
|
|
457
|
+
return intPart === "" ? 0 : Number(intPart);
|
|
458
|
+
}
|
|
459
|
+
var RATE_LIMITS = [
|
|
460
|
+
["5h", "five_hour"],
|
|
461
|
+
["7d", "seven_day"],
|
|
462
|
+
["spend", "spend_limit"]
|
|
463
|
+
];
|
|
464
|
+
function rateRows(source) {
|
|
465
|
+
const limits = field$1(source, "rate_limits");
|
|
466
|
+
const rows = [];
|
|
467
|
+
for (const [label, key] of RATE_LIMITS) {
|
|
468
|
+
const limit = member(limits, key);
|
|
469
|
+
if (!isRecord(limit) || Object.keys(limit).length === 0) continue;
|
|
470
|
+
rows.push({
|
|
471
|
+
label,
|
|
472
|
+
pctText: scalar(limit, "used_percentage", "0"),
|
|
473
|
+
resets: Number(scalar(limit, "resets_at", "0"))
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
return rows;
|
|
477
|
+
}
|
|
478
|
+
function parseRow(payload) {
|
|
479
|
+
const root = JSON.parse(payload);
|
|
480
|
+
const hitText = scalar(root, "prompt_cache.hit_ratio", "");
|
|
481
|
+
return {
|
|
482
|
+
agent: scalar(root, "agent.name", ""),
|
|
483
|
+
cost: numberField(root, "cost.total_cost_usd"),
|
|
484
|
+
ctxSize: numberField(root, "context_window.context_window_size"),
|
|
485
|
+
dir: scalar(root, "workspace.current_dir", ""),
|
|
486
|
+
durationMs: numberField(root, "cost.total_duration_ms"),
|
|
487
|
+
effort: scalar(root, "effort.level", ""),
|
|
488
|
+
expires: numberField(root, "prompt_cache.expires_at"),
|
|
489
|
+
hit: hitText === "" ? null : Number(hitText),
|
|
490
|
+
la: numberField(root, "cost.total_lines_added"),
|
|
491
|
+
lr: numberField(root, "cost.total_lines_removed"),
|
|
492
|
+
model: scalar(root, "model.display_name", ""),
|
|
493
|
+
pct: pctField(root),
|
|
494
|
+
prn: scalar(root, "pr.number", ""),
|
|
495
|
+
prs: scalar(root, "pr.review_state", ""),
|
|
496
|
+
rateRows: rateRows(root),
|
|
497
|
+
think: scalar(root, "thinking.enabled", "false") === "true",
|
|
498
|
+
tokens: numberField(root, "context_window.total_input_tokens"),
|
|
499
|
+
ttl: scalar(root, "prompt_cache.ttl", ""),
|
|
500
|
+
vim: scalar(root, "vim.mode", ""),
|
|
501
|
+
warm: scalar(root, "prompt_cache.warm", "false") === "true",
|
|
502
|
+
wt: scalar(root, "worktree.name", ""),
|
|
503
|
+
styleName: scalar(root, "output_style.name", "")
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region src/render/awk.ts
|
|
508
|
+
var TEN = 10n;
|
|
509
|
+
function exactParts(ax) {
|
|
510
|
+
const view = /* @__PURE__ */ new DataView(/* @__PURE__ */ new ArrayBuffer(8));
|
|
511
|
+
view.setFloat64(0, ax);
|
|
512
|
+
const hi = view.getUint32(0);
|
|
513
|
+
const lo = view.getUint32(4);
|
|
514
|
+
const biased = hi >>> 20 & 2047;
|
|
515
|
+
const frac = BigInt(hi & 1048575) << 32n | BigInt(lo);
|
|
516
|
+
if (biased === 0) return {
|
|
517
|
+
exp: -1074,
|
|
518
|
+
mant: frac
|
|
519
|
+
};
|
|
520
|
+
return {
|
|
521
|
+
exp: biased - 1075,
|
|
522
|
+
mant: frac | 1n << 52n
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
function roundScaled(ax, d) {
|
|
526
|
+
const { exp, mant } = exactParts(ax);
|
|
527
|
+
const pow10 = TEN ** BigInt(d);
|
|
528
|
+
const num = exp >= 0 ? (mant << BigInt(exp)) * pow10 : mant * pow10;
|
|
529
|
+
const den = exp >= 0 ? 1n : 1n << BigInt(-exp);
|
|
530
|
+
const q = num / den;
|
|
531
|
+
const twice = num % den * 2n;
|
|
532
|
+
if (twice > den || twice === den && q % 2n === 1n) return q + 1n;
|
|
533
|
+
return q;
|
|
534
|
+
}
|
|
535
|
+
function fmtFixed(x, d) {
|
|
536
|
+
const q = roundScaled(Math.abs(x), d);
|
|
537
|
+
const sign = x < 0 && q !== 0n ? "-" : "";
|
|
538
|
+
const digits = q.toString();
|
|
539
|
+
if (d === 0) return `${sign}${digits}`;
|
|
540
|
+
return `${sign}${digits.length <= d ? "0" : digits.slice(0, digits.length - d)}.${digits.padStart(d + 1, "0").slice(-d)}`;
|
|
541
|
+
}
|
|
542
|
+
function pad2(n) {
|
|
543
|
+
return String(n).padStart(2, "0");
|
|
544
|
+
}
|
|
545
|
+
function fmtK(n, d) {
|
|
546
|
+
return `${fmtFixed(n / 1e3, d)}k`;
|
|
547
|
+
}
|
|
548
|
+
function fmtM(n) {
|
|
549
|
+
return `${fmtFixed(n / 1e6, 0)}M`;
|
|
550
|
+
}
|
|
551
|
+
//#endregion
|
|
552
|
+
//#region src/render/segments.ts
|
|
553
|
+
var BLUE = "\x1B[34m";
|
|
554
|
+
var CYAN$1 = "\x1B[36m";
|
|
555
|
+
var DIM = "\x1B[2m";
|
|
556
|
+
var GREEN$1 = "\x1B[32m";
|
|
557
|
+
var PURPLE = "\x1B[35m";
|
|
558
|
+
var RED$1 = "\x1B[31m";
|
|
559
|
+
var RESET$1 = "\x1B[0m";
|
|
560
|
+
var YELLOW$1 = "\x1B[33m";
|
|
561
|
+
var BOLD = "\x1B[1m";
|
|
562
|
+
var GRAY = "\x1B[38;2;68;71;90m";
|
|
563
|
+
var MARK = "\x1B[1;97m";
|
|
564
|
+
var SHORT_AT = 15;
|
|
565
|
+
var PART = [
|
|
566
|
+
"▏",
|
|
567
|
+
"▎",
|
|
568
|
+
"▍",
|
|
569
|
+
"▌",
|
|
570
|
+
"▋",
|
|
571
|
+
"▊",
|
|
572
|
+
"▉",
|
|
573
|
+
"█"
|
|
574
|
+
];
|
|
575
|
+
var HEAT = [
|
|
576
|
+
"\x1B[38;2;48;242;36m",
|
|
577
|
+
"\x1B[38;2;63;242;36m",
|
|
578
|
+
"\x1B[38;2;79;242;36m",
|
|
579
|
+
"\x1B[38;2;95;242;36m",
|
|
580
|
+
"\x1B[38;2;111;242;36m",
|
|
581
|
+
"\x1B[38;2;126;242;36m",
|
|
582
|
+
"\x1B[38;2;142;242;36m",
|
|
583
|
+
"\x1B[38;2;158;242;36m",
|
|
584
|
+
"\x1B[38;2;173;242;36m",
|
|
585
|
+
"\x1B[38;2;189;242;36m",
|
|
586
|
+
"\x1B[38;2;205;242;36m",
|
|
587
|
+
"\x1B[38;2;220;242;36m",
|
|
588
|
+
"\x1B[38;2;236;242;36m",
|
|
589
|
+
"\x1B[38;2;242;232;36m",
|
|
590
|
+
"\x1B[38;2;242;216;36m",
|
|
591
|
+
"\x1B[38;2;242;200;36m",
|
|
592
|
+
"\x1B[38;2;242;185;36m",
|
|
593
|
+
"\x1B[38;2;242;169;36m",
|
|
594
|
+
"\x1B[38;2;242;153;36m",
|
|
595
|
+
"\x1B[38;2;242;138;36m",
|
|
596
|
+
"\x1B[38;2;242;122;36m",
|
|
597
|
+
"\x1B[38;2;242;106;36m",
|
|
598
|
+
"\x1B[38;2;242;91;36m",
|
|
599
|
+
"\x1B[38;2;242;75;36m",
|
|
600
|
+
"\x1B[38;2;242;59;36m",
|
|
601
|
+
"\x1B[38;2;242;44;36m"
|
|
602
|
+
];
|
|
603
|
+
function trunc(n) {
|
|
604
|
+
return Math.trunc(n);
|
|
605
|
+
}
|
|
606
|
+
function modelPlain({ model }) {
|
|
607
|
+
return model === "" ? "" : `${CYAN$1}${model}${RESET$1}`;
|
|
608
|
+
}
|
|
609
|
+
function modelBlock({ model }) {
|
|
610
|
+
return model === "" ? "" : `\x1b[48;5;61m\x1b[38;5;231m ${model} \x1b[0m`;
|
|
611
|
+
}
|
|
612
|
+
function modelPill({ model }) {
|
|
613
|
+
return model === "" ? "" : `\x1b[48;5;61m\x1b[38;5;231m\u{e0b6} ${model} \u{e0b4}\x1b[0m`;
|
|
614
|
+
}
|
|
615
|
+
function modelZen({ model }) {
|
|
616
|
+
return model === "" ? "" : `${DIM}${model.replace(/[A-Z]/g, (c) => c.toLowerCase())}${RESET$1}`;
|
|
617
|
+
}
|
|
618
|
+
function effortPlain({ row }) {
|
|
619
|
+
return row.effort === "" ? "" : `${CYAN$1}${row.effort}${RESET$1}`;
|
|
620
|
+
}
|
|
621
|
+
function effortDim({ row }) {
|
|
622
|
+
return row.effort === "" ? "" : `${DIM}${row.effort}${RESET$1}`;
|
|
623
|
+
}
|
|
624
|
+
function statePill(label, bg, fg) {
|
|
625
|
+
return `\x1b[48;5;${bg}m\x1b[38;5;${fg}m\u{e0b6} ${label} \u{e0b4}\x1b[0m`;
|
|
626
|
+
}
|
|
627
|
+
function statePills({ row }) {
|
|
628
|
+
let out = "";
|
|
629
|
+
if (row.vim !== "") out = row.vim === "INSERT" ? statePill(row.vim, 97, 16) : statePill(row.vim, 240, 231);
|
|
630
|
+
if (row.think) out += ` ${statePill("THINK", 66, 16)}`;
|
|
631
|
+
if (row.agent !== "") out += ` ${statePill(row.agent, 60, 231)}`;
|
|
632
|
+
if (row.wt !== "") out += ` ${statePill(`wt:${row.wt}`, 131, 231)}`;
|
|
633
|
+
if (row.styleName !== "" && row.styleName !== "default") out += ` ${statePill(row.styleName, 95, 16)}`;
|
|
634
|
+
return out;
|
|
635
|
+
}
|
|
636
|
+
function splitPath(raw, home) {
|
|
637
|
+
let p = raw;
|
|
638
|
+
if (home !== "" && p.startsWith(home)) p = `~${p.slice(home.length)}`;
|
|
639
|
+
return {
|
|
640
|
+
lead: p.startsWith("/") ? "/" : "",
|
|
641
|
+
parts: p.split("/").filter((part) => part !== ""),
|
|
642
|
+
text: p
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function pathInit(raw, home) {
|
|
646
|
+
const { lead, parts, text } = splitPath(raw, home);
|
|
647
|
+
const n = parts.length;
|
|
648
|
+
if (n <= 2) return text;
|
|
649
|
+
let out = `${lead}${parts[0]}`;
|
|
650
|
+
for (let i = 1; i < n - 1; i++) {
|
|
651
|
+
const ini = parts[i].startsWith(".") ? parts[i].slice(0, 2) : parts[i].slice(0, 1);
|
|
652
|
+
out += `/${ini}`;
|
|
653
|
+
}
|
|
654
|
+
return `${out}/${parts[n - 1]}`;
|
|
655
|
+
}
|
|
656
|
+
function pathTail(raw, home, lim) {
|
|
657
|
+
const { lead, parts, text } = splitPath(raw, home);
|
|
658
|
+
const cnt = parts.length;
|
|
659
|
+
if (cnt <= lim || cnt <= 1) return text;
|
|
660
|
+
let out = `${lead}${parts[0]}/…`;
|
|
661
|
+
for (let i = cnt - lim; i < cnt; i++) out += `/${parts[i]}`;
|
|
662
|
+
return out;
|
|
663
|
+
}
|
|
664
|
+
function cwdInit({ home, row }) {
|
|
665
|
+
const { text } = splitPath(row.dir, home);
|
|
666
|
+
return Buffer.byteLength(text, "utf8") > SHORT_AT ? pathInit(row.dir, home) : text;
|
|
667
|
+
}
|
|
668
|
+
function cwdFull({ home, row }) {
|
|
669
|
+
return splitPath(row.dir, home).text;
|
|
670
|
+
}
|
|
671
|
+
function cwdTail({ home, row }) {
|
|
672
|
+
return pathTail(row.dir, home, 1);
|
|
673
|
+
}
|
|
674
|
+
function baseName(dir) {
|
|
675
|
+
return dir.slice(dir.lastIndexOf("/") + 1);
|
|
676
|
+
}
|
|
677
|
+
function cwdBase({ row }) {
|
|
678
|
+
return baseName(row.dir);
|
|
679
|
+
}
|
|
680
|
+
function cwdIcon({ row }) {
|
|
681
|
+
return `\u{f07b} ${baseName(row.dir)}`;
|
|
682
|
+
}
|
|
683
|
+
function branchInitials({ git }) {
|
|
684
|
+
const branch = git.branch;
|
|
685
|
+
if (branch === "") return "";
|
|
686
|
+
if (Buffer.byteLength(branch, "utf8") <= SHORT_AT) return branch;
|
|
687
|
+
const parts = branch.split("/").filter((part) => part !== "");
|
|
688
|
+
if (parts.length <= 1) return branch;
|
|
689
|
+
let out = "";
|
|
690
|
+
for (let i = 0; i < parts.length - 1; i++) out += `${parts[i].slice(0, 1)}/`;
|
|
691
|
+
return `${out}${parts[parts.length - 1]}`;
|
|
692
|
+
}
|
|
693
|
+
function branchFull({ git }) {
|
|
694
|
+
return git.branch;
|
|
695
|
+
}
|
|
696
|
+
function branchLast({ git }) {
|
|
697
|
+
return git.branch.slice(git.branch.lastIndexOf("/") + 1);
|
|
698
|
+
}
|
|
699
|
+
function branchIcon({ git }) {
|
|
700
|
+
return git.branch === "" ? "" : `\u{e0a0} ${git.branch}`;
|
|
701
|
+
}
|
|
702
|
+
function statusCounts({ git }) {
|
|
703
|
+
if (git.branch === "") return "";
|
|
704
|
+
let c = "";
|
|
705
|
+
if (git.staged > 0) c = `${GREEN$1}+${git.staged}${RESET$1}`;
|
|
706
|
+
if (git.modified > 0) c = `${c} ${YELLOW$1}~${git.modified}${RESET$1}`;
|
|
707
|
+
return c;
|
|
708
|
+
}
|
|
709
|
+
function statusIcons({ git }) {
|
|
710
|
+
if (git.branch === "") return "";
|
|
711
|
+
let out = "";
|
|
712
|
+
let sep = "";
|
|
713
|
+
if (git.staged > 0) {
|
|
714
|
+
out += `${GREEN$1}●${git.staged}${RESET$1}`;
|
|
715
|
+
sep = " ";
|
|
716
|
+
}
|
|
717
|
+
if (git.modified > 0) {
|
|
718
|
+
out += `${sep}${YELLOW$1}✎${git.modified}${RESET$1}`;
|
|
719
|
+
sep = " ";
|
|
720
|
+
}
|
|
721
|
+
if (git.untracked > 0) {
|
|
722
|
+
out += `${sep}${CYAN$1}+${git.untracked}${RESET$1}`;
|
|
723
|
+
sep = " ";
|
|
724
|
+
}
|
|
725
|
+
if (git.stashes > 0) out += `${sep}${PURPLE}⚑${git.stashes}${RESET$1}`;
|
|
726
|
+
return out;
|
|
727
|
+
}
|
|
728
|
+
function aheadArrows({ git }) {
|
|
729
|
+
let out = "";
|
|
730
|
+
if (git.ahead > 0) out = `${GREEN$1}↑${git.ahead}${RESET$1}`;
|
|
731
|
+
if (git.behind > 0) out += ` ${RED$1}↓${git.behind}${RESET$1}`;
|
|
732
|
+
return out;
|
|
733
|
+
}
|
|
734
|
+
function prBadge({ row }) {
|
|
735
|
+
if (row.prn === "") return "";
|
|
736
|
+
let col;
|
|
737
|
+
let mark;
|
|
738
|
+
switch (row.prs) {
|
|
739
|
+
case "approved":
|
|
740
|
+
col = GREEN$1;
|
|
741
|
+
mark = "✓";
|
|
742
|
+
break;
|
|
743
|
+
case "changes_requested":
|
|
744
|
+
col = RED$1;
|
|
745
|
+
mark = "✗";
|
|
746
|
+
break;
|
|
747
|
+
case "pending":
|
|
748
|
+
col = YELLOW$1;
|
|
749
|
+
mark = "⏳";
|
|
750
|
+
break;
|
|
751
|
+
default:
|
|
752
|
+
col = "\x1B[90m";
|
|
753
|
+
mark = "◌";
|
|
754
|
+
}
|
|
755
|
+
return `${BLUE}#${row.prn}${RESET$1} ${col}${mark} ${row.prs}${RESET$1}`;
|
|
756
|
+
}
|
|
757
|
+
function barFlatAt(input, w) {
|
|
758
|
+
const pct = input.row.pct;
|
|
759
|
+
let f = trunc(pct * w / 100);
|
|
760
|
+
if (f > w) f = w;
|
|
761
|
+
return `${pct >= 90 ? RED$1 : pct >= 70 ? YELLOW$1 : GREEN$1}${"█".repeat(f)}${"░".repeat(w - f)}${RESET$1}`;
|
|
762
|
+
}
|
|
763
|
+
function barFlat(input) {
|
|
764
|
+
return barFlatAt(input, 10);
|
|
765
|
+
}
|
|
766
|
+
function barGauge({ row }) {
|
|
767
|
+
const pct = row.pct;
|
|
768
|
+
const p = Math.max(0, Math.min(100, pct));
|
|
769
|
+
const full = trunc(p * 26 / 100);
|
|
770
|
+
const idx = trunc(p * 26 % 100 * 8 / 100);
|
|
771
|
+
const lead = p >= 50 ? `\x1b[38;2;242;${trunc(4845 * (15e3 + 1683 * (100 - p)) / 2e6)};36m` : `\x1b[38;2;${trunc(4845 * (185e3 - 1683 * (100 - p)) / 2e6)};242;36m`;
|
|
772
|
+
const moon = [
|
|
773
|
+
"○",
|
|
774
|
+
"◔",
|
|
775
|
+
"◑",
|
|
776
|
+
"◕",
|
|
777
|
+
"●"
|
|
778
|
+
][Math.min(trunc(p / 20), 4)];
|
|
779
|
+
let bar = "";
|
|
780
|
+
for (let i = 0; i < 26; i++) {
|
|
781
|
+
let col;
|
|
782
|
+
let cell;
|
|
783
|
+
if (i === 20) {
|
|
784
|
+
col = MARK;
|
|
785
|
+
cell = i <= full ? "┃" : "│";
|
|
786
|
+
} else if (i <= full) {
|
|
787
|
+
col = HEAT[i];
|
|
788
|
+
cell = i === full ? PART[idx] : "█";
|
|
789
|
+
} else {
|
|
790
|
+
col = GRAY;
|
|
791
|
+
cell = "░";
|
|
792
|
+
}
|
|
793
|
+
bar += `${col}${cell}`;
|
|
794
|
+
}
|
|
795
|
+
return `${moon} ${lead}${bar}${RESET$1} ${MARK}${pct}%${RESET$1}`;
|
|
796
|
+
}
|
|
797
|
+
function tokensFull({ row }) {
|
|
798
|
+
const t = row.tokens >= 1e3 ? fmtK(row.tokens, 1) : String(row.tokens);
|
|
799
|
+
let c = String(row.ctxSize);
|
|
800
|
+
if (row.ctxSize >= 1e6) c = fmtM(row.ctxSize);
|
|
801
|
+
else if (row.ctxSize >= 1e3) c = fmtK(row.ctxSize, 0);
|
|
802
|
+
return `${t}/${c}`;
|
|
803
|
+
}
|
|
804
|
+
function tokensCompact({ row }) {
|
|
805
|
+
return row.tokens >= 1e3 ? fmtK(row.tokens, 0) : String(row.tokens);
|
|
806
|
+
}
|
|
807
|
+
function tokensFree({ row }) {
|
|
808
|
+
const free = row.ctxSize - row.tokens;
|
|
809
|
+
return `${DIM}${free >= 1e3 ? fmtK(free, 0) : String(free)} free${RESET$1}`;
|
|
810
|
+
}
|
|
811
|
+
function cacheHitPct(row) {
|
|
812
|
+
return row.hit === null ? null : trunc(row.hit * 100);
|
|
813
|
+
}
|
|
814
|
+
function cacheHCol(hp) {
|
|
815
|
+
return hp >= 90 ? GREEN$1 : hp >= 50 ? YELLOW$1 : RED$1;
|
|
816
|
+
}
|
|
817
|
+
function cacheHit({ row }) {
|
|
818
|
+
const hp = cacheHitPct(row);
|
|
819
|
+
return hp === null ? "" : `${cacheHCol(hp)}⚡${hp}%${RESET$1}`;
|
|
820
|
+
}
|
|
821
|
+
function cacheColdin({ now, row }) {
|
|
822
|
+
const hp = cacheHitPct(row);
|
|
823
|
+
if (hp === null) return "";
|
|
824
|
+
if (row.warm && row.expires > now) {
|
|
825
|
+
const mins = trunc((row.expires - now) / 60);
|
|
826
|
+
return `${cacheHCol(hp)}⚡${hp}%${RESET$1} ${DIM}· cold in ${mins}m${RESET$1}`;
|
|
827
|
+
}
|
|
828
|
+
return `❄ ${DIM}cold · ${hp}%${RESET$1}`;
|
|
829
|
+
}
|
|
830
|
+
function cacheFuse({ now, row }) {
|
|
831
|
+
if (row.ttl === "" || row.expires === 0) return "";
|
|
832
|
+
const span = row.ttl === "1h" ? 3600 : 300;
|
|
833
|
+
let left = row.expires - now;
|
|
834
|
+
if (left < 0) left = 0;
|
|
835
|
+
if (left <= 0) return `${RED$1}❄ cold${RESET$1}`;
|
|
836
|
+
const full = trunc(left * 10 / span);
|
|
837
|
+
const rem = left * 10 % span;
|
|
838
|
+
let bar = left * 4 > span ? GREEN$1 : left * 25 > span * 2 ? YELLOW$1 : RED$1;
|
|
839
|
+
for (let i = 0; i < 10; i++) if (i < full) bar += "▰";
|
|
840
|
+
else if (i === full && rem * 20 > span) bar += PART[trunc(rem * 8 / span)];
|
|
841
|
+
else bar += `${DIM}▱`;
|
|
842
|
+
return `${bar}${RESET$1} ${DIM}${pad2(trunc(left / 60))}:${pad2(left % 60)}${RESET$1}`;
|
|
843
|
+
}
|
|
844
|
+
function costPlain({ row }) {
|
|
845
|
+
return row.cost >= .005 ? `${YELLOW$1}$${fmtFixed(row.cost, 2)}${RESET$1}` : "";
|
|
846
|
+
}
|
|
847
|
+
function costBurn(input) {
|
|
848
|
+
const hr = input.row.durationMs / 36e5;
|
|
849
|
+
const burn = hr > .02 ? fmtFixed(input.row.cost / hr, 2) : "0";
|
|
850
|
+
let out = costPlain(input);
|
|
851
|
+
if (burn !== "0.00") out += ` ${DIM}· $${burn}/hr${RESET$1}`;
|
|
852
|
+
return out;
|
|
853
|
+
}
|
|
854
|
+
function durationClock({ row }) {
|
|
855
|
+
return `${trunc(row.durationMs / 6e4)}m${pad2(trunc(row.durationMs % 6e4 / 1e3))}s`;
|
|
856
|
+
}
|
|
857
|
+
function durationHours({ row }) {
|
|
858
|
+
const m = trunc(row.durationMs / 6e4);
|
|
859
|
+
return `${trunc(m / 60)}h${pad2(m % 60)}m`;
|
|
860
|
+
}
|
|
861
|
+
function linesDiffstat({ row }) {
|
|
862
|
+
if (row.la <= 0 && row.lr <= 0) return "";
|
|
863
|
+
return `${GREEN$1}+${row.la}${RESET$1}${DIM}/${RESET$1}${RED$1}−${row.lr}${RESET$1}`;
|
|
864
|
+
}
|
|
865
|
+
function rateStrip({ now, row }) {
|
|
866
|
+
const segs = [];
|
|
867
|
+
for (const limit of row.rateRows) {
|
|
868
|
+
const [intpText, fracp = ""] = limit.pctText.split(".");
|
|
869
|
+
let f = 0;
|
|
870
|
+
let scale = 1;
|
|
871
|
+
for (const digit of fracp) {
|
|
872
|
+
f = f * 10 + Number(digit);
|
|
873
|
+
scale *= 10;
|
|
874
|
+
}
|
|
875
|
+
const intp = Number(intpText);
|
|
876
|
+
const value = intp * scale + f;
|
|
877
|
+
const col = value < 70 * scale ? GREEN$1 : value < 90 * scale ? YELLOW$1 : RED$1;
|
|
878
|
+
const full = trunc(value * 14 / (100 * scale));
|
|
879
|
+
const idx = trunc(value * 14 % (100 * scale) * 8 / (100 * scale));
|
|
880
|
+
let bar = col;
|
|
881
|
+
for (let i = 0; i < 14; i++) if (i < full) bar += "█";
|
|
882
|
+
else if (i === full) bar += PART[idx];
|
|
883
|
+
else bar += `${DIM}░`;
|
|
884
|
+
const lbl = fracp === "" || f * 2 < scale ? intp : f * 2 > scale ? intp + 1 : intp + intp % 2;
|
|
885
|
+
const s = limit.resets - now;
|
|
886
|
+
let dur;
|
|
887
|
+
if (s >= 3600) dur = `${trunc(s / 3600)}h${pad2(trunc(s % 3600 / 60))}m`;
|
|
888
|
+
else {
|
|
889
|
+
const sec = (s % 60 + 60) % 60;
|
|
890
|
+
dur = `${trunc((s - sec) / 60)}m${pad2(sec)}s`;
|
|
891
|
+
}
|
|
892
|
+
segs.push(`${DIM}${limit.label}${RESET$1} ${bar}${RESET$1} ${BOLD}${lbl}%${RESET$1} ${DIM}· resets ${dur}${RESET$1}`);
|
|
893
|
+
}
|
|
894
|
+
return segs.join(` ${DIM}│${RESET$1} `);
|
|
895
|
+
}
|
|
896
|
+
function emptySegment() {
|
|
897
|
+
return "";
|
|
898
|
+
}
|
|
899
|
+
function barFlat6(input) {
|
|
900
|
+
return barFlatAt(input, 6);
|
|
901
|
+
}
|
|
902
|
+
function barFlat4(input) {
|
|
903
|
+
return barFlatAt(input, 4);
|
|
904
|
+
}
|
|
905
|
+
function barPercent({ row }) {
|
|
906
|
+
return `${row.pct}%`;
|
|
907
|
+
}
|
|
908
|
+
var SEGMENTS = {
|
|
909
|
+
ahead: {
|
|
910
|
+
arrows: aheadArrows,
|
|
911
|
+
none: emptySegment
|
|
912
|
+
},
|
|
913
|
+
bar: {
|
|
914
|
+
flat4: barFlat4,
|
|
915
|
+
flat6: barFlat6,
|
|
916
|
+
flat: barFlat,
|
|
917
|
+
gauge: barGauge,
|
|
918
|
+
none: emptySegment,
|
|
919
|
+
percent: barPercent
|
|
920
|
+
},
|
|
921
|
+
branch: {
|
|
922
|
+
full: branchFull,
|
|
923
|
+
icon: branchIcon,
|
|
924
|
+
initials: branchInitials,
|
|
925
|
+
last: branchLast,
|
|
926
|
+
none: emptySegment
|
|
927
|
+
},
|
|
928
|
+
cache: {
|
|
929
|
+
coldin: cacheColdin,
|
|
930
|
+
fuse: cacheFuse,
|
|
931
|
+
hit: cacheHit,
|
|
932
|
+
none: emptySegment
|
|
933
|
+
},
|
|
934
|
+
cost: {
|
|
935
|
+
burn: costBurn,
|
|
936
|
+
none: emptySegment,
|
|
937
|
+
plain: costPlain
|
|
938
|
+
},
|
|
939
|
+
cwd: {
|
|
940
|
+
base: cwdBase,
|
|
941
|
+
full: cwdFull,
|
|
942
|
+
icon: cwdIcon,
|
|
943
|
+
init: cwdInit,
|
|
944
|
+
tail: cwdTail
|
|
945
|
+
},
|
|
946
|
+
duration: {
|
|
947
|
+
clock: durationClock,
|
|
948
|
+
hours: durationHours,
|
|
949
|
+
none: emptySegment
|
|
950
|
+
},
|
|
951
|
+
effort: {
|
|
952
|
+
dim: effortDim,
|
|
953
|
+
hidden: emptySegment,
|
|
954
|
+
plain: effortPlain
|
|
955
|
+
},
|
|
956
|
+
lines: {
|
|
957
|
+
diffstat: linesDiffstat,
|
|
958
|
+
none: emptySegment
|
|
959
|
+
},
|
|
960
|
+
model: {
|
|
961
|
+
block: modelBlock,
|
|
962
|
+
pill: modelPill,
|
|
963
|
+
plain: modelPlain,
|
|
964
|
+
zen: modelZen
|
|
965
|
+
},
|
|
966
|
+
pr: {
|
|
967
|
+
badge: prBadge,
|
|
968
|
+
none: emptySegment
|
|
969
|
+
},
|
|
970
|
+
rate: {
|
|
971
|
+
none: emptySegment,
|
|
972
|
+
strip: rateStrip
|
|
973
|
+
},
|
|
974
|
+
state: {
|
|
975
|
+
none: emptySegment,
|
|
976
|
+
pills: statePills
|
|
977
|
+
},
|
|
978
|
+
status: {
|
|
979
|
+
counts: statusCounts,
|
|
980
|
+
icons: statusIcons,
|
|
981
|
+
none: emptySegment
|
|
982
|
+
},
|
|
983
|
+
tokens: {
|
|
984
|
+
compact: tokensCompact,
|
|
985
|
+
full: tokensFull,
|
|
986
|
+
free: tokensFree,
|
|
987
|
+
none: emptySegment
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
function renderSegment(item, alt, input) {
|
|
991
|
+
const segment = SEGMENTS[item]?.[alt];
|
|
992
|
+
return segment === void 0 ? "" : segment(input);
|
|
993
|
+
}
|
|
994
|
+
function styleSeparators(alt) {
|
|
995
|
+
switch (alt) {
|
|
996
|
+
case "bare": return {
|
|
997
|
+
join: " ",
|
|
998
|
+
sep: " "
|
|
999
|
+
};
|
|
1000
|
+
case "dim": return {
|
|
1001
|
+
join: " ",
|
|
1002
|
+
sep: `${DIM} │ ${RESET$1}`
|
|
1003
|
+
};
|
|
1004
|
+
case "dots": return {
|
|
1005
|
+
join: " · ",
|
|
1006
|
+
sep: " · "
|
|
1007
|
+
};
|
|
1008
|
+
default: return {
|
|
1009
|
+
join: " ",
|
|
1010
|
+
sep: " │ "
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
//#endregion
|
|
1015
|
+
//#region src/render/engine.ts
|
|
1016
|
+
var RUNG_ITEMS = Object.keys(RUNG_ORDERS);
|
|
1017
|
+
function stripSgr(text) {
|
|
1018
|
+
return text.replace(/\x1b\[[0-9;]*m/g, "");
|
|
1019
|
+
}
|
|
1020
|
+
function vlen$1(text) {
|
|
1021
|
+
const plain = stripSgr(text);
|
|
1022
|
+
let n = Array.from(plain).length;
|
|
1023
|
+
for (const ch of plain) if (ch === "⚡") n += 1;
|
|
1024
|
+
return n;
|
|
1025
|
+
}
|
|
1026
|
+
function warn(message) {
|
|
1027
|
+
process.stderr.write(`${message}\n`);
|
|
1028
|
+
}
|
|
1029
|
+
function stripModelSuffix(name) {
|
|
1030
|
+
const at = name.lastIndexOf("[");
|
|
1031
|
+
const cut = at === -1 ? name : name.slice(0, at);
|
|
1032
|
+
return cut.endsWith(" ") ? cut.slice(0, -1) : cut;
|
|
1033
|
+
}
|
|
1034
|
+
function resolvePicks(picks) {
|
|
1035
|
+
const resolved = {};
|
|
1036
|
+
for (const spec of ITEMS) {
|
|
1037
|
+
const pick = picks?.[spec.item] ?? spec.default;
|
|
1038
|
+
if (spec.alternatives.includes(pick)) resolved[spec.item] = pick;
|
|
1039
|
+
else {
|
|
1040
|
+
warn(`statusline: ${spec.item}=${pick} is not available, using ${spec.item}=${spec.default}`);
|
|
1041
|
+
resolved[spec.item] = spec.default;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
return resolved;
|
|
1045
|
+
}
|
|
1046
|
+
function parseLayout(layout, known) {
|
|
1047
|
+
let parsed;
|
|
1048
|
+
try {
|
|
1049
|
+
parsed = parseClusters(layout);
|
|
1050
|
+
} catch (e) {
|
|
1051
|
+
warn(`statusline: ${e.message}`);
|
|
1052
|
+
return [];
|
|
1053
|
+
}
|
|
1054
|
+
const clusters = [];
|
|
1055
|
+
for (const words of parsed) {
|
|
1056
|
+
const kept = words.filter((word) => {
|
|
1057
|
+
if (known.has(word)) return true;
|
|
1058
|
+
warn(`statusline: layout item '${word}' is not available, skipped`);
|
|
1059
|
+
return false;
|
|
1060
|
+
});
|
|
1061
|
+
if (kept.length > 0) clusters.push(kept);
|
|
1062
|
+
}
|
|
1063
|
+
return clusters;
|
|
1064
|
+
}
|
|
1065
|
+
function renderStatusline(input) {
|
|
1066
|
+
const row = parseRow(input.payload);
|
|
1067
|
+
const git = readGit(row.dir, { home: input.home });
|
|
1068
|
+
const picks = resolvePicks(input.picks);
|
|
1069
|
+
const { join, sep } = styleSeparators(picks.style);
|
|
1070
|
+
const clusters = parseLayout(input.layout ?? "{model effort state} {cwd branch status ahead pr} {bar tokens cache} {cost} {duration} {lines} {rate}", new Set(ITEMS.map((spec) => spec.item)));
|
|
1071
|
+
const wrapAt = clusters.length > 2 ? 2 : 1;
|
|
1072
|
+
let width = input.columns;
|
|
1073
|
+
if (width === void 0 || !Number.isInteger(width) || width < 0) width = 200;
|
|
1074
|
+
if (width < 20) width = 20;
|
|
1075
|
+
const avail = width - 3;
|
|
1076
|
+
let model = row.model;
|
|
1077
|
+
const initialRungs = RUNG_ITEMS.map((item) => [item, picks[item]]);
|
|
1078
|
+
function segmentInput() {
|
|
1079
|
+
return {
|
|
1080
|
+
git,
|
|
1081
|
+
home: input.home,
|
|
1082
|
+
model,
|
|
1083
|
+
now: input.now,
|
|
1084
|
+
row
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
function compose(mode) {
|
|
1088
|
+
const from = mode === "l2" ? wrapAt : 0;
|
|
1089
|
+
const to = mode === "l1" ? wrapAt : clusters.length;
|
|
1090
|
+
let line = "";
|
|
1091
|
+
let first = true;
|
|
1092
|
+
for (let ci = from; ci < to; ci++) {
|
|
1093
|
+
let cseg = "";
|
|
1094
|
+
let cf = true;
|
|
1095
|
+
for (const item of clusters[ci]) {
|
|
1096
|
+
const out = renderSegment(item, picks[item], segmentInput());
|
|
1097
|
+
if (out === "") continue;
|
|
1098
|
+
if (cf) {
|
|
1099
|
+
cseg = out;
|
|
1100
|
+
cf = false;
|
|
1101
|
+
} else cseg += join + out;
|
|
1102
|
+
}
|
|
1103
|
+
if (cseg === "") continue;
|
|
1104
|
+
if (first) {
|
|
1105
|
+
line = cseg;
|
|
1106
|
+
first = false;
|
|
1107
|
+
} else line += sep + cseg;
|
|
1108
|
+
}
|
|
1109
|
+
return line;
|
|
1110
|
+
}
|
|
1111
|
+
function fits(line) {
|
|
1112
|
+
return vlen$1(line) <= avail;
|
|
1113
|
+
}
|
|
1114
|
+
function demote(item, target) {
|
|
1115
|
+
const order = RUNG_ORDERS[item];
|
|
1116
|
+
if (order === void 0) return;
|
|
1117
|
+
const at = order.indexOf(picks[item]);
|
|
1118
|
+
if (at === -1) return;
|
|
1119
|
+
if (order.slice(at + 1).includes(target)) picks[item] = target;
|
|
1120
|
+
}
|
|
1121
|
+
function resetRungs() {
|
|
1122
|
+
model = row.model;
|
|
1123
|
+
for (const [item, pick] of initialRungs) picks[item] = pick;
|
|
1124
|
+
}
|
|
1125
|
+
function applyStep(step) {
|
|
1126
|
+
if (step[0] === "model" && step[1] === "strip") model = stripModelSuffix(model);
|
|
1127
|
+
else demote(step[0], step[1]);
|
|
1128
|
+
}
|
|
1129
|
+
function fit(mode, steps) {
|
|
1130
|
+
let out = compose(mode);
|
|
1131
|
+
if (fits(out)) return out;
|
|
1132
|
+
for (const step of steps) {
|
|
1133
|
+
applyStep(step);
|
|
1134
|
+
out = compose(mode);
|
|
1135
|
+
if (fits(out)) return out;
|
|
1136
|
+
}
|
|
1137
|
+
return out;
|
|
1138
|
+
}
|
|
1139
|
+
function emit(line) {
|
|
1140
|
+
return `${input.noColor === true ? stripSgr(line) : line}\n`;
|
|
1141
|
+
}
|
|
1142
|
+
const full = fit("full", FULL_STEPS);
|
|
1143
|
+
if (fits(full)) return emit(full);
|
|
1144
|
+
resetRungs();
|
|
1145
|
+
const l1 = fit("l1", L1_STEPS);
|
|
1146
|
+
const l2 = fit("l2", L2_STEPS);
|
|
1147
|
+
return emit(l1) + emit(l2);
|
|
1148
|
+
}
|
|
1149
|
+
//#endregion
|
|
1150
|
+
//#region src/render/panel.ts
|
|
1151
|
+
var CYAN = "\x1B[36m";
|
|
1152
|
+
var GREEN = "\x1B[32m";
|
|
1153
|
+
var RED = "\x1B[31m";
|
|
1154
|
+
var RESET = "\x1B[0m";
|
|
1155
|
+
var YELLOW = "\x1B[33m";
|
|
1156
|
+
var MS_THRESHOLD = 2e11;
|
|
1157
|
+
var STEPS = [
|
|
1158
|
+
["descd", 1],
|
|
1159
|
+
["durd", 1],
|
|
1160
|
+
["statd", 1],
|
|
1161
|
+
["barb", 6],
|
|
1162
|
+
["modeld", 1],
|
|
1163
|
+
["barb", 4],
|
|
1164
|
+
["barb", 0],
|
|
1165
|
+
["modeld", 2],
|
|
1166
|
+
["statd", 2]
|
|
1167
|
+
];
|
|
1168
|
+
function field(source, key, fallback) {
|
|
1169
|
+
return orElse(source[key], fallback);
|
|
1170
|
+
}
|
|
1171
|
+
function cell(value) {
|
|
1172
|
+
return tsvEscape(jqText(value));
|
|
1173
|
+
}
|
|
1174
|
+
function truncateDesc(desc) {
|
|
1175
|
+
const chars = Array.from(desc);
|
|
1176
|
+
return chars.length > 24 ? `${chars.slice(0, 23).join("")}…` : desc;
|
|
1177
|
+
}
|
|
1178
|
+
function intValue(text) {
|
|
1179
|
+
return /^-?\d+$/.test(text) ? Number(text) : null;
|
|
1180
|
+
}
|
|
1181
|
+
function stylePick(picks) {
|
|
1182
|
+
const spec = specFor("style");
|
|
1183
|
+
if (spec === void 0) return "plain";
|
|
1184
|
+
const wanted = picks?.style ?? "";
|
|
1185
|
+
const alt = wanted === "" ? spec.default : wanted;
|
|
1186
|
+
if (!spec.alternatives.includes(alt)) {
|
|
1187
|
+
warn(`statusline: style=${alt} is not available, using style=${spec.default}`);
|
|
1188
|
+
return spec.default;
|
|
1189
|
+
}
|
|
1190
|
+
return alt;
|
|
1191
|
+
}
|
|
1192
|
+
function availColumns(tick) {
|
|
1193
|
+
const text = jqText(isRecord(tick) ? field(tick, "columns", 200) : 200);
|
|
1194
|
+
const columns = /^\d+$/.test(text) ? Number(text) : 200;
|
|
1195
|
+
return Math.max(columns, 20) - 1;
|
|
1196
|
+
}
|
|
1197
|
+
function extractFields(task) {
|
|
1198
|
+
return {
|
|
1199
|
+
ctx: cell(field(task, "contextWindowSize", 0)),
|
|
1200
|
+
desc: cell(truncateDesc(jqText(field(task, "description", "")))),
|
|
1201
|
+
effort: cell(field(task, "effort", "")),
|
|
1202
|
+
id: cell(field(task, "id", "")),
|
|
1203
|
+
label: cell(field(task, "label", "")),
|
|
1204
|
+
model: cell(field(task, "model", "")),
|
|
1205
|
+
name: cell(field(task, "name", "")),
|
|
1206
|
+
start: cell(field(task, "startTime", 0)),
|
|
1207
|
+
tokens: cell(field(task, "tokenCount", 0))
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
function makeBar(pct, width) {
|
|
1211
|
+
let f = Math.trunc(pct * width / 100);
|
|
1212
|
+
if (f > width) f = width;
|
|
1213
|
+
let bar = "";
|
|
1214
|
+
for (let i = 0; i < width; i++) bar += i < f ? "█" : "░";
|
|
1215
|
+
return bar;
|
|
1216
|
+
}
|
|
1217
|
+
function duration(startText, now) {
|
|
1218
|
+
const start = intValue(startText);
|
|
1219
|
+
if (start === null || start <= 0) return "";
|
|
1220
|
+
const seconds = start > MS_THRESHOLD ? Math.trunc(start / 1e3) : start;
|
|
1221
|
+
const elapsed = Math.trunc(now) - seconds;
|
|
1222
|
+
if (elapsed < 0) return "";
|
|
1223
|
+
return `${Math.trunc(elapsed / 60)}m${pad2(Math.trunc(elapsed % 60))}s`;
|
|
1224
|
+
}
|
|
1225
|
+
function rowFormats(fields, now) {
|
|
1226
|
+
const tokens = intValue(fields.tokens) ?? 0;
|
|
1227
|
+
const ctx = intValue(fields.ctx) ?? 0;
|
|
1228
|
+
let ctxText = fields.ctx;
|
|
1229
|
+
if (ctx >= 1e6) ctxText = fmtM(ctx);
|
|
1230
|
+
else if (ctx >= 1e3) ctxText = fmtK(ctx, 0);
|
|
1231
|
+
return {
|
|
1232
|
+
ctx: ctxText,
|
|
1233
|
+
dur: duration(fields.start, now),
|
|
1234
|
+
tickTok: tokens >= 1e3 ? fmtK(tokens, 0) : String(tokens),
|
|
1235
|
+
tokens: tokens >= 1e3 ? fmtK(tokens, 1) : String(tokens)
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
function renderRow(fields, formats, state, sep) {
|
|
1239
|
+
let s = fields.label === "" ? fields.name : fields.label;
|
|
1240
|
+
if (fields.desc !== "" && fields.desc !== fields.label && state.descd === 0) s += ` ${fields.desc}`;
|
|
1241
|
+
if (fields.model !== "") {
|
|
1242
|
+
let name = fields.model;
|
|
1243
|
+
if (state.modeld >= 1) name = stripModelSuffix(name);
|
|
1244
|
+
const effort = state.modeld >= 2 ? "" : fields.effort;
|
|
1245
|
+
if (effort !== "") name += ` ${effort}`;
|
|
1246
|
+
s += `${sep}${CYAN}${name}${RESET}`;
|
|
1247
|
+
}
|
|
1248
|
+
const ctx = intValue(fields.ctx);
|
|
1249
|
+
if (ctx !== null && ctx > 0) {
|
|
1250
|
+
const tokens = intValue(fields.tokens) ?? 0;
|
|
1251
|
+
const pct = Math.trunc(tokens * 100 / ctx);
|
|
1252
|
+
const barColor = pct >= 90 ? RED : pct >= 70 ? YELLOW : GREEN;
|
|
1253
|
+
if (state.barb > 0) s += `${sep}${barColor}${makeBar(pct, state.barb)}${RESET} ${pct}%`;
|
|
1254
|
+
else s += `${sep}${barColor}${pct}%${RESET}`;
|
|
1255
|
+
const stats = state.statd === 1 ? formats.tickTok : state.statd === 2 ? "" : `${formats.tokens}/${formats.ctx}`;
|
|
1256
|
+
if (stats !== "") s += ` ${stats}`;
|
|
1257
|
+
}
|
|
1258
|
+
if (formats.dur !== "" && state.durd === 0) s += `${sep}${formats.dur}`;
|
|
1259
|
+
return s;
|
|
1260
|
+
}
|
|
1261
|
+
function vlen(text) {
|
|
1262
|
+
return Array.from(stripSgr(text)).length;
|
|
1263
|
+
}
|
|
1264
|
+
function fitRow(fields, formats, sep, avail) {
|
|
1265
|
+
const state = {
|
|
1266
|
+
barb: 10,
|
|
1267
|
+
descd: 0,
|
|
1268
|
+
durd: 0,
|
|
1269
|
+
modeld: 0,
|
|
1270
|
+
statd: 0
|
|
1271
|
+
};
|
|
1272
|
+
let out = renderRow(fields, formats, state, sep);
|
|
1273
|
+
if (vlen(out) <= avail) return out;
|
|
1274
|
+
for (const [key, value] of STEPS) {
|
|
1275
|
+
state[key] = value;
|
|
1276
|
+
out = renderRow(fields, formats, state, sep);
|
|
1277
|
+
if (vlen(out) <= avail) return out;
|
|
1278
|
+
}
|
|
1279
|
+
return out;
|
|
1280
|
+
}
|
|
1281
|
+
function emitLine(id, content) {
|
|
1282
|
+
return `{"id":${JSON.stringify(id)},"content":${JSON.stringify(content)}}`;
|
|
1283
|
+
}
|
|
1284
|
+
function renderPanel(input) {
|
|
1285
|
+
const tick = JSON.parse(input.payload);
|
|
1286
|
+
const avail = availColumns(tick);
|
|
1287
|
+
const sep = styleSeparators(stylePick(input.picks)).sep;
|
|
1288
|
+
const tasks = isRecord(tick) && Array.isArray(tick.tasks) ? tick.tasks : [];
|
|
1289
|
+
let out = "";
|
|
1290
|
+
for (const task of tasks) {
|
|
1291
|
+
if (!isRecord(task)) continue;
|
|
1292
|
+
const fields = extractFields(task);
|
|
1293
|
+
if (fields.id === "") continue;
|
|
1294
|
+
const row = fitRow(fields, rowFormats(fields, input.now), sep, avail);
|
|
1295
|
+
const content = input.noColor === true ? stripSgr(row) : row;
|
|
1296
|
+
out += `${emitLine(fields.id, content)}\n`;
|
|
1297
|
+
}
|
|
1298
|
+
return out;
|
|
1299
|
+
}
|
|
1300
|
+
//#endregion
|
|
1301
|
+
//#region src/themes.ts
|
|
1302
|
+
var THEMES = {
|
|
1303
|
+
quiet: {
|
|
1304
|
+
layout: "{model cwd}",
|
|
1305
|
+
summary: "model and directory, nothing else",
|
|
1306
|
+
variants: {
|
|
1307
|
+
cwd: "tail",
|
|
1308
|
+
model: "zen",
|
|
1309
|
+
style: "bare"
|
|
1310
|
+
}
|
|
1311
|
+
},
|
|
1312
|
+
classic: {
|
|
1313
|
+
layout: DEFAULT_LAYOUT,
|
|
1314
|
+
summary: "the shipped defaults, named",
|
|
1315
|
+
variants: {}
|
|
1316
|
+
},
|
|
1317
|
+
lean: {
|
|
1318
|
+
layout: DEFAULT_LAYOUT,
|
|
1319
|
+
summary: "text only, no graphics",
|
|
1320
|
+
variants: {
|
|
1321
|
+
model: "plain",
|
|
1322
|
+
effort: "dim",
|
|
1323
|
+
state: "none",
|
|
1324
|
+
cwd: "init",
|
|
1325
|
+
branch: "initials",
|
|
1326
|
+
status: "counts",
|
|
1327
|
+
ahead: "arrows",
|
|
1328
|
+
pr: "badge",
|
|
1329
|
+
bar: "percent",
|
|
1330
|
+
tokens: "full",
|
|
1331
|
+
cache: "hit",
|
|
1332
|
+
cost: "plain",
|
|
1333
|
+
duration: "clock",
|
|
1334
|
+
lines: "diffstat",
|
|
1335
|
+
rate: "none",
|
|
1336
|
+
style: "dots"
|
|
1337
|
+
}
|
|
1338
|
+
},
|
|
1339
|
+
rich: {
|
|
1340
|
+
layout: DEFAULT_LAYOUT,
|
|
1341
|
+
summary: "every gauge and counter",
|
|
1342
|
+
variants: {
|
|
1343
|
+
model: "pill",
|
|
1344
|
+
effort: "plain",
|
|
1345
|
+
state: "pills",
|
|
1346
|
+
cwd: "icon",
|
|
1347
|
+
branch: "icon",
|
|
1348
|
+
status: "icons",
|
|
1349
|
+
ahead: "arrows",
|
|
1350
|
+
pr: "badge",
|
|
1351
|
+
bar: "gauge",
|
|
1352
|
+
tokens: "full",
|
|
1353
|
+
cache: "fuse",
|
|
1354
|
+
cost: "burn",
|
|
1355
|
+
duration: "clock",
|
|
1356
|
+
lines: "diffstat",
|
|
1357
|
+
rate: "strip",
|
|
1358
|
+
style: "plain"
|
|
1359
|
+
}
|
|
1360
|
+
},
|
|
1361
|
+
custom: {
|
|
1362
|
+
layout: DEFAULT_LAYOUT,
|
|
1363
|
+
summary: "bare; you decide everything",
|
|
1364
|
+
variants: {
|
|
1365
|
+
model: "zen",
|
|
1366
|
+
effort: "hidden",
|
|
1367
|
+
state: "none",
|
|
1368
|
+
cwd: "base",
|
|
1369
|
+
branch: "none",
|
|
1370
|
+
status: "none",
|
|
1371
|
+
ahead: "none",
|
|
1372
|
+
pr: "none",
|
|
1373
|
+
bar: "none",
|
|
1374
|
+
tokens: "none",
|
|
1375
|
+
cache: "none",
|
|
1376
|
+
cost: "none",
|
|
1377
|
+
duration: "none",
|
|
1378
|
+
lines: "none",
|
|
1379
|
+
rate: "none",
|
|
1380
|
+
style: "bare"
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
};
|
|
1384
|
+
//#endregion
|
|
1385
|
+
//#region src/render/theme.ts
|
|
1386
|
+
function resolvePaint(input) {
|
|
1387
|
+
const name = input.theme;
|
|
1388
|
+
const theme = name === void 0 ? void 0 : THEMES[name];
|
|
1389
|
+
if (name !== void 0 && theme === void 0) warn(`statusline: theme=${name} is not a known theme, ignored`);
|
|
1390
|
+
return {
|
|
1391
|
+
layout: input.layout ?? theme?.layout ?? "{model effort state} {cwd branch status ahead pr} {bar tokens cache} {cost} {duration} {lines} {rate}",
|
|
1392
|
+
picks: {
|
|
1393
|
+
...theme?.variants ?? {},
|
|
1394
|
+
...input.picks
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
//#endregion
|
|
1399
|
+
//#region src/render/entry.ts
|
|
1400
|
+
function readStdin() {
|
|
1401
|
+
return new Promise((resolve, reject) => {
|
|
1402
|
+
let payload = "";
|
|
1403
|
+
process.stdin.setEncoding("utf8");
|
|
1404
|
+
process.stdin.on("data", (chunk) => {
|
|
1405
|
+
payload += chunk;
|
|
1406
|
+
});
|
|
1407
|
+
process.stdin.on("end", () => {
|
|
1408
|
+
resolve(payload);
|
|
1409
|
+
});
|
|
1410
|
+
process.stdin.on("error", reject);
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
function columnsFromEnv(text) {
|
|
1414
|
+
if (text === void 0 || text === "" || !/^\d+$/.test(text)) return;
|
|
1415
|
+
return Number(text);
|
|
1416
|
+
}
|
|
1417
|
+
async function main() {
|
|
1418
|
+
const argv = parseArgv(process.argv.slice(2));
|
|
1419
|
+
for (const warning of argv.warnings) process.stderr.write(`${warning}\n`);
|
|
1420
|
+
const payload = await readStdin();
|
|
1421
|
+
const env = process.env;
|
|
1422
|
+
const home = env.HOME ?? "";
|
|
1423
|
+
const noColor = (env.NO_COLOR ?? "") !== "";
|
|
1424
|
+
const now = argv.now ?? Math.floor(Date.now() / 1e3);
|
|
1425
|
+
const paint = resolvePaint(argv);
|
|
1426
|
+
if (argv.mode === "panel") {
|
|
1427
|
+
capturePayload(home, "tick", payload);
|
|
1428
|
+
process.stdout.write(renderPanel({
|
|
1429
|
+
now,
|
|
1430
|
+
payload,
|
|
1431
|
+
picks: paint.picks,
|
|
1432
|
+
noColor
|
|
1433
|
+
}));
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
capturePayload(home, "main", payload);
|
|
1437
|
+
const columns = columnsFromEnv(env.COLUMNS);
|
|
1438
|
+
process.stdout.write(renderStatusline({
|
|
1439
|
+
home,
|
|
1440
|
+
now,
|
|
1441
|
+
payload,
|
|
1442
|
+
picks: paint.picks,
|
|
1443
|
+
noColor,
|
|
1444
|
+
layout: paint.layout,
|
|
1445
|
+
...columns === void 0 ? {} : { columns }
|
|
1446
|
+
}));
|
|
1447
|
+
}
|
|
1448
|
+
await main();
|
|
1449
|
+
//#endregion
|
|
1450
|
+
export {};
|
|
1451
|
+
|
|
1452
|
+
//# sourceMappingURL=render.mjs.map
|