claude-doom-statusbar 0.1.1 → 0.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.
- package/assets/images/hud.png +0 -0
- package/package.json +1 -1
- package/presets/full.toml +6 -6
- package/src/render.js +23 -16
- package/src/statusline.js +17 -4
package/assets/images/hud.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-doom-statusbar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "DOOM-inspired status bar for the Claude Code CLI — a mugshot that tracks session health, plus usage, model, project, system, and a live subagent list.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/presets/full.toml
CHANGED
|
@@ -34,12 +34,12 @@ metric = [
|
|
|
34
34
|
type = "box"
|
|
35
35
|
title = "PROJECT"
|
|
36
36
|
metric = [
|
|
37
|
-
{ id = "
|
|
38
|
-
{ id = "
|
|
39
|
-
{
|
|
40
|
-
{ id = "git.
|
|
41
|
-
{ id = "loc.churn",
|
|
42
|
-
{ id = "pr.state",
|
|
37
|
+
{ id = "session.name", render = "text", icon = "🎮" },
|
|
38
|
+
{ id = "loc.cwd", render = "text", icon = "📁" },
|
|
39
|
+
{ id = "git.branch", render = "text", icon = "🌿" },
|
|
40
|
+
{ id = "git.work", render = "text" }, # ✎ files ⇅ pull/push
|
|
41
|
+
{ id = "loc.churn", render = "text", icon = "📝" },
|
|
42
|
+
{ id = "pr.state", render = "text", icon = "⇧ " },
|
|
43
43
|
]
|
|
44
44
|
|
|
45
45
|
[[segment]]
|
package/src/render.js
CHANGED
|
@@ -33,13 +33,14 @@ export const SAMPLE = {
|
|
|
33
33
|
"usage.reset5h": "2h13m", "usage.reset7d": "3d4h", "sys.session": "5h55m", "loc.churn": "+185 / -62",
|
|
34
34
|
"context.hp": 78, "ratelimit.5h": 64, "ratelimit.7d": 31, "cost.total": "$1.83",
|
|
35
35
|
"git.branch": "main", "git.behind": "↓2", "git.ahead": "↑3", "git.status": "3",
|
|
36
|
-
"
|
|
36
|
+
"git.work": "✎ 3 ⇅ ↓2 ↑3", "session.name": "doom-hud-demo",
|
|
37
|
+
"pr.state": "#1234", "loc.cwd": "claude-doom-statusbar",
|
|
37
38
|
"act.subagents": [["hook events", "2m13s"], ["find configs", "12s"]], "act.agents": "2",
|
|
38
39
|
"act.tasklist": [
|
|
39
|
-
{ mark:"
|
|
40
|
-
{ mark:"
|
|
41
|
-
{ mark:"
|
|
42
|
-
{ mark:"
|
|
40
|
+
{ mark:"✅", markRgb: OK, text:"scaffold project" },
|
|
41
|
+
{ mark:"✅", markRgb: OK, text:"render engine" },
|
|
42
|
+
{ mark:"❌", markRgb: CRIT, text:"port PIL alpha" },
|
|
43
|
+
{ mark:"⏩", markRgb: null, text:"statusline values" },
|
|
43
44
|
{ mark:"🎯", markRgb: null, text:"hook bus" },
|
|
44
45
|
{ mark:"🎯", markRgb: null, text:"installer" },
|
|
45
46
|
],
|
|
@@ -58,7 +59,8 @@ export function vlen(s) {
|
|
|
58
59
|
let n = 0;
|
|
59
60
|
for (const ch of String(s).replace(ANSI_RE, "")) {
|
|
60
61
|
const cp = ch.codePointAt(0);
|
|
61
|
-
n += (cp >= 0x1f300 && cp <= 0x1faff) || (cp >= 0x23e9 && cp <= 0x23ec)
|
|
62
|
+
n += (cp >= 0x1f300 && cp <= 0x1faff) || (cp >= 0x23e9 && cp <= 0x23ec)
|
|
63
|
+
|| cp === 0x2705 || cp === 0x274c ? 2 : 1; // ✅ ❌ are emoji-presentation (2 cols)
|
|
62
64
|
}
|
|
63
65
|
return n;
|
|
64
66
|
}
|
|
@@ -368,30 +370,35 @@ export function buildBar(cfg, target, spriteFor) {
|
|
|
368
370
|
const items = VALUES[m.id] || [];
|
|
369
371
|
const H = totalRows - (headers ? 1 : 0);
|
|
370
372
|
const boundary = items.filter((it) => !Array.isArray(it) &&
|
|
371
|
-
(it.mark === "
|
|
373
|
+
(it.mark === "✅" || it.mark === "❌")).length; // settled count (ignored for top anchor)
|
|
372
374
|
const win = scrollWindow(items.length, H, m.anchor || "top", boundary);
|
|
373
375
|
const shown = items.slice(win.start, win.start + H);
|
|
374
376
|
shown.forEach((item, k) => {
|
|
375
377
|
const first = k === 0, last = k === shown.length - 1;
|
|
376
|
-
const
|
|
378
|
+
const marker = first && win.up > 0 ? `↑${win.up}` : last && win.down > 0 ? `↓${win.down}` : "";
|
|
379
|
+
const tail = marker ? " " + marker : ""; // right-aligned scroll marker (gap + ↑k/↓k)
|
|
380
|
+
const tailW = vlen(tail);
|
|
377
381
|
let body;
|
|
378
382
|
if (Array.isArray(item)) { // [left, right] (agents)
|
|
379
|
-
const right = f(TEXT) + String(item[1]);
|
|
380
|
-
const
|
|
381
|
-
const labelMax = Math.max(0, w - vlen(
|
|
383
|
+
const right = f(TEXT) + String(item[1]) + (marker ? f(TEXT) + tail : "");
|
|
384
|
+
const rightW = vlen(String(item[1])) + tailW;
|
|
385
|
+
const labelMax = Math.max(0, w - vlen(lbl) - rightW - 1); // 1 = min gap
|
|
382
386
|
let label = String(item[0]);
|
|
383
387
|
if (vlen(label) > labelMax) label = [...label].slice(0, Math.max(0, labelMax - 1)).join("") + "…";
|
|
384
|
-
const left =
|
|
385
|
-
const room = Math.max(0, w - vlen(left) -
|
|
388
|
+
const left = lbl + f(TEXT) + label;
|
|
389
|
+
const room = Math.max(0, w - vlen(left) - rightW);
|
|
386
390
|
body = left + " ".repeat(room) + right;
|
|
387
391
|
} else { // {mark, markRgb, text} (tasks)
|
|
388
392
|
const markCol = item.markRgb ? f(item.markRgb) : f(TEXT);
|
|
393
|
+
const m = String(item.mark);
|
|
394
|
+
const mPad = m + (vlen(m) < 2 ? " " : ""); // normalize mark to 2 cols so text aligns
|
|
389
395
|
let text = String(item.text);
|
|
390
|
-
const head =
|
|
391
|
-
const max = w - vlen(
|
|
396
|
+
const head = markCol + mPad + " " + f(TEXT);
|
|
397
|
+
const max = w - vlen(mPad) - 1 - tailW; // reserve gap + marker on the right
|
|
392
398
|
if (vlen(text) > max) text = [...text].slice(0, Math.max(0, max - 1)).join("") + "…";
|
|
393
399
|
body = head + text;
|
|
394
|
-
body += " ".repeat(Math.max(0, w - vlen(body)));
|
|
400
|
+
body += " ".repeat(Math.max(0, w - tailW - vlen(body)));
|
|
401
|
+
if (tail) body += f(TEXT) + tail;
|
|
395
402
|
}
|
|
396
403
|
col.push(bgsgrBox(boxRgb) + " " + body + " " + RESET);
|
|
397
404
|
});
|
package/src/statusline.js
CHANGED
|
@@ -41,6 +41,10 @@ function git(cwd, ...args) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Clip a display label to at most `n` code points, ending with … when truncated,
|
|
45
|
+
// so an oversized repo or branch name can't blow up the PROJECT box width.
|
|
46
|
+
const clip = (s, n) => ([...String(s)].length > n ? [...String(s)].slice(0, n - 1).join("") + "…" : String(s));
|
|
47
|
+
|
|
44
48
|
function _dur(secsF) {
|
|
45
49
|
let secs = Math.max(0, Math.trunc(secsF));
|
|
46
50
|
const d = Math.floor(secs / 86400); secs %= 86400;
|
|
@@ -141,12 +145,15 @@ export function buildValues(data) {
|
|
|
141
145
|
let repoUrl = "";
|
|
142
146
|
if (repo.host && repo.owner && repo.name) repoUrl = `https://${repo.host}/${repo.owner}/${repo.name}`;
|
|
143
147
|
|
|
148
|
+
const sname = data.session_name || data.session_id; // session_name only set via /rename or --name
|
|
149
|
+
if (sname) v["session.name"] = clip(sname, 24);
|
|
150
|
+
|
|
144
151
|
const cwd = data.cwd || (data.workspace || {}).current_dir;
|
|
145
152
|
if (cwd) {
|
|
146
|
-
const name = path.basename(cwd.replace(/[/\\]+$/, "")) || cwd;
|
|
153
|
+
const name = clip(path.basename(cwd.replace(/[/\\]+$/, "")) || cwd, 24);
|
|
147
154
|
try { v["loc.cwd"] = _link(name, pathToFileURL(cwd).href); } catch { v["loc.cwd"] = name; }
|
|
148
155
|
const br = git(cwd, "branch", "--show-current");
|
|
149
|
-
if (br) v["git.branch"] = repoUrl ? _link(
|
|
156
|
+
if (br) { const brLbl = clip(br, 24); v["git.branch"] = repoUrl ? _link(brLbl, `${repoUrl}/tree/${br}`) : brLbl; }
|
|
150
157
|
const lr = git(cwd, "rev-list", "--count", "--left-right", "@{u}...HEAD");
|
|
151
158
|
if (lr && lr.includes("\t")) {
|
|
152
159
|
const [behind, ahead] = lr.split("\t");
|
|
@@ -154,6 +161,12 @@ export function buildValues(data) {
|
|
|
154
161
|
}
|
|
155
162
|
const st = git(cwd, "status", "--porcelain");
|
|
156
163
|
if (st !== null) v["git.status"] = String(st.split("\n").filter((l) => l.trim()).length);
|
|
164
|
+
// Merge changed-file count + pull/push onto one line (icons baked in, like model.mode):
|
|
165
|
+
// "✎ <files> ⇅ ↓<behind> ↑<ahead>" — files first, then pull/push.
|
|
166
|
+
const work = [];
|
|
167
|
+
if (v["git.status"] !== undefined) work.push(`✎ ${v["git.status"]}`);
|
|
168
|
+
if (v["git.behind"] !== undefined) work.push(`⇅ ${v["git.behind"]} ${v["git.ahead"]}`);
|
|
169
|
+
if (work.length) v["git.work"] = work.join(" ");
|
|
157
170
|
}
|
|
158
171
|
|
|
159
172
|
const pr = data.pr || {};
|
|
@@ -218,8 +231,8 @@ function sysValues(cwd) {
|
|
|
218
231
|
const PERM = { plan: "📋 plan", auto: "⏩ auto", acceptEdits: "⏩ auto", bypassPermissions: "⏩ bypass" };
|
|
219
232
|
const OK_RGB = [96, 200, 104]; // matches render.js OK (done, green)
|
|
220
233
|
const CRIT_RGB = [224, 84, 64]; // matches render.js CRIT (deleted, red)
|
|
221
|
-
const TASK_MARK = { completed: ["
|
|
222
|
-
const TASK_ORDER = { completed: 0, deleted:
|
|
234
|
+
const TASK_MARK = { completed: ["✅", OK_RGB], deleted: ["❌", CRIT_RGB], in_progress: ["⏩", null], pending: ["🎯", null] };
|
|
235
|
+
const TASK_ORDER = { completed: 0, deleted: 0, in_progress: 1, pending: 2 }; // settled (done+deleted, by time) first, then open
|
|
223
236
|
|
|
224
237
|
export function activityValues(st, now) {
|
|
225
238
|
const v = {};
|