@enigmax/dashboard 0.1.7 → 0.1.9
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/index.html +72 -0
- package/package.json +1 -1
package/assets/index.html
CHANGED
|
@@ -141,6 +141,15 @@
|
|
|
141
141
|
.linput { flex: 1; min-width: 0; background: var(--surface2); color: var(--text); border: 1px solid var(--border); border-radius: 7px; padding: 5px 10px; font-size: 12px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
142
142
|
.laddb { background: var(--surface2); color: var(--accent); border: 1px solid var(--border); border-radius: 7px; padding: 5px 14px; font-size: 12px; cursor: pointer; font-family: inherit; }
|
|
143
143
|
.laddb:hover { border-color: var(--accent); }
|
|
144
|
+
/* Claude-style usage window gauges (current session / weekly all / weekly Sonnet). */
|
|
145
|
+
.wgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; margin-bottom: 18px; }
|
|
146
|
+
.wcard { background: var(--surface2); border: 1px solid var(--border); border-radius: 12px; padding: 16px; }
|
|
147
|
+
.wtitle { font-size: 13px; font-weight: 600; color: var(--text); }
|
|
148
|
+
.wreset { font-size: 12px; color: var(--muted); margin-top: 2px; }
|
|
149
|
+
.wval { font-size: 22px; font-weight: 600; color: var(--accent); margin-top: 12px; }
|
|
150
|
+
.wval.muted { font-size: 14px; font-weight: 500; color: var(--muted); }
|
|
151
|
+
.wset { display: flex; gap: 6px; margin-top: 10px; }
|
|
152
|
+
.wlimit { flex: 1; min-width: 0; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 7px; padding: 4px 8px; font-size: 12px; }
|
|
144
153
|
.sys-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 0 24px; }
|
|
145
154
|
.sys-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--surface2); font-size: 13px; }
|
|
146
155
|
.sys-item .k { color: var(--muted); }
|
|
@@ -276,6 +285,9 @@
|
|
|
276
285
|
<p>Measured token consumption and estimated cost, read-only from your own Claude Code session transcripts (<code>~/.claude/projects</code>). Cost is an estimate from a per-model price table; real spend is billed by Anthropic. Nothing is sent anywhere.</p>
|
|
277
286
|
</div>
|
|
278
287
|
<div class="panel" id="usagePanel" style="display:none">
|
|
288
|
+
<h2 style="margin:0 0 4px">Usage limits <small>same windows Claude Code shows</small></h2>
|
|
289
|
+
<div class="sub" style="margin-bottom:12px">Cards marked <span class="tag meas">live</span> show the <b>real % and reset</b> Anthropic reports (the same as Claude's own UI), captured by the local proxy from your <code>enigma claude</code> traffic - no plan limit needed. Until the proxy has seen a request, the card falls back to tokens used, or a % against a plan limit you set (on the card, or <code>enigma config plan-weekly-limit <tokens></code>). Per-model live data appears only when Anthropic sends it.</div>
|
|
290
|
+
<div id="uWindows"></div>
|
|
279
291
|
<div class="grid" style="margin-bottom:16px">
|
|
280
292
|
<div class="card"><div class="label">Est. Cost</div><div id="uCost" class="value accent">-</div><div class="foot">All sessions, per-model pricing</div></div>
|
|
281
293
|
<div class="card"><div class="label">Cache Saved</div><div id="uCacheMoney" class="value good">-</div><div class="foot">Est. from prompt caching</div></div>
|
|
@@ -515,6 +527,64 @@
|
|
|
515
527
|
// Real tool-usage panel (Claude Code transcripts). null = the opt-in flag is off.
|
|
516
528
|
function shortProject(p) { p = String(p || ""); return p.length > 44 ? "..." + p.slice(-41) : p; }
|
|
517
529
|
|
|
530
|
+
// --- Claude-style usage windows (current session / weekly all / weekly Sonnet) ---
|
|
531
|
+
function fmtResetIn(ms) {
|
|
532
|
+
const d = ms - Date.now();
|
|
533
|
+
if (d <= 0) return "now";
|
|
534
|
+
const h = Math.floor(d / 3600000), m = Math.round((d % 3600000) / 60000);
|
|
535
|
+
return h ? ("in " + h + " hr " + m + " min") : ("in " + m + " min");
|
|
536
|
+
}
|
|
537
|
+
function fmtResetAt(ms) {
|
|
538
|
+
const dt = new Date(ms);
|
|
539
|
+
return "Resets " + dt.toLocaleDateString([], { weekday: "short" }) + " " + dt.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
|
|
540
|
+
}
|
|
541
|
+
function windowCard(w, sub) {
|
|
542
|
+
const reset = w.kind === "session"
|
|
543
|
+
? (w.resetsAt ? "Resets " + fmtResetIn(w.resetsAt) : "No active session")
|
|
544
|
+
: (w.resetsAt ? fmtResetAt(w.resetsAt) : "");
|
|
545
|
+
let body;
|
|
546
|
+
if (sub === "weeklySonnet" && (w.used || 0) === 0) {
|
|
547
|
+
body = '<div class="wval muted">You haven\'t used Sonnet yet</div>';
|
|
548
|
+
} else if (w.pct != null) {
|
|
549
|
+
const p = Math.round(w.pct);
|
|
550
|
+
const col = w.pct >= 90 ? "var(--accent2)" : "var(--accent)";
|
|
551
|
+
body = '<div class="wval">' + p + '% used</div>'
|
|
552
|
+
+ '<div class="bar-track" style="margin-top:8px"><div class="bar-fill" style="width:' + Math.min(100, w.pct) + '%;background:' + col + '"></div></div>';
|
|
553
|
+
} else {
|
|
554
|
+
body = '<div class="wval">' + fmt(w.used || 0) + ' tok</div>'
|
|
555
|
+
+ '<div class="wset"><input type="number" min="0" class="wlimit" data-plan="' + sub + '" placeholder="set limit (tokens)">'
|
|
556
|
+
+ '<button type="button" class="laddb" data-plan="' + sub + '">Set</button></div>';
|
|
557
|
+
}
|
|
558
|
+
const live = w.live ? ' <span class="tag meas">live</span>' : "";
|
|
559
|
+
return '<div class="wcard"><div class="wtitle">' + esc(w.label) + live + '</div><div class="wreset">' + esc(reset) + '</div>' + body + '</div>';
|
|
560
|
+
}
|
|
561
|
+
function renderWindows(u) {
|
|
562
|
+
const w = u && u.windows;
|
|
563
|
+
if (!w) { $("uWindows").innerHTML = ""; return; }
|
|
564
|
+
$("uWindows").innerHTML = '<div class="wgrid">'
|
|
565
|
+
+ windowCard(w.session, "session")
|
|
566
|
+
+ windowCard(w.weeklyAll, "weekly")
|
|
567
|
+
+ windowCard(w.weeklySonnet, "weeklySonnet")
|
|
568
|
+
+ "</div>";
|
|
569
|
+
}
|
|
570
|
+
async function planSet(key, value) {
|
|
571
|
+
try { await fetch("/api/plan", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ key, value: Number(value) }) }); refreshAll(true); }
|
|
572
|
+
catch { /* leave as-is */ }
|
|
573
|
+
}
|
|
574
|
+
function wireUsage() {
|
|
575
|
+
const el = $("uWindows");
|
|
576
|
+
el.addEventListener("click", (e) => {
|
|
577
|
+
const b = e.target.closest("button[data-plan]"); if (!b) return;
|
|
578
|
+
const inp = el.querySelector('input.wlimit[data-plan="' + b.dataset.plan + '"]');
|
|
579
|
+
if (inp && inp.value) planSet(b.dataset.plan, inp.value);
|
|
580
|
+
});
|
|
581
|
+
el.addEventListener("keydown", (e) => {
|
|
582
|
+
if (e.key !== "Enter") return;
|
|
583
|
+
const inp = e.target.closest("input.wlimit");
|
|
584
|
+
if (inp && inp.value) planSet(inp.dataset.plan, inp.value);
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
518
588
|
// One token/cost breakdown table (By Model / By Project), sorted by cost then tokens.
|
|
519
589
|
function renderUsageTable(el, map, pending, label) {
|
|
520
590
|
const entries = Object.entries(map || {}).sort((a, b) => ((b[1].cost || 0) - (a[1].cost || 0)) || ((b[1].input + b[1].output) - (a[1].input + a[1].output)));
|
|
@@ -540,6 +610,7 @@
|
|
|
540
610
|
if (!u) { panel.style.display = "none"; hint.style.display = "block"; return; }
|
|
541
611
|
hint.style.display = "none";
|
|
542
612
|
panel.style.display = "block";
|
|
613
|
+
renderWindows(u);
|
|
543
614
|
$("uCost").textContent = fmtUsd(u.cost || 0);
|
|
544
615
|
$("uCacheMoney").textContent = fmtUsd(cacheMoney(u.cacheRead || 0));
|
|
545
616
|
$("uCacheRead").textContent = fmt(u.cacheRead || 0);
|
|
@@ -1180,6 +1251,7 @@
|
|
|
1180
1251
|
wireSettings();
|
|
1181
1252
|
wireSkills();
|
|
1182
1253
|
wireAccounts();
|
|
1254
|
+
wireUsage();
|
|
1183
1255
|
$("cfgExport").addEventListener("click", exportConfig);
|
|
1184
1256
|
$("cfgImport").addEventListener("click", importConfig);
|
|
1185
1257
|
$("updateBtn").addEventListener("click", runUpdate);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enigmax/dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Local browser dashboard UI for enigma: the static page and chart assets enigma serves on its loopback dashboard (savings, real tool usage, in-browser settings). Installed on demand by enigma-cli; not a runtime dependency.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|