@enigmax/dashboard 0.1.42 → 0.1.43

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 (2) hide show
  1. package/assets/index.html +30 -11
  2. package/package.json +1 -1
package/assets/index.html CHANGED
@@ -252,6 +252,8 @@
252
252
  .aicons { display: inline-flex; gap: 5px; align-items: center; vertical-align: middle; margin-left: 6px; }
253
253
  .aicon { display: inline-flex; align-items: center; color: var(--muted); }
254
254
  .aicon svg { width: 14px; height: 14px; display: block; }
255
+ /* Brand icon + label, reused by the savings breakdowns and the recent-compressions table. */
256
+ .srctag { display: inline-flex; align-items: center; gap: 7px; }
255
257
  .set-ic { vertical-align: middle; margin-right: 7px; }
256
258
  /* List settings (guard block/allow globs, custom secret patterns): chips + add input. A list row spans the full grid width. */
257
259
  .set-row.list { grid-column: 1 / -1; align-items: stretch; flex-direction: column; }
@@ -505,7 +507,7 @@
505
507
  </div>
506
508
  </div>
507
509
  <div class="useg-controls" style="display:flex;gap:14px;flex-wrap:wrap;align-items:center;margin-bottom:16px">
508
- <label class="useg-pick"><span>Provider</span><select id="uProvider" class="modal-input"></select></label>
510
+ <label class="useg-pick"><span>Provider</span><span id="uProviderIcon" class="aicon" aria-hidden="true"></span><select id="uProvider" class="modal-input"></select></label>
509
511
  <label class="useg-pick" id="uAccountWrap"><span>Account</span><select id="uAccount" class="modal-input"></select></label>
510
512
  </div>
511
513
  <div class="panel" id="usagePanel" style="display:none">
@@ -616,8 +618,8 @@
616
618
  <div class="set-grid">
617
619
  <div class="set-row">
618
620
  <div class="set-meta"><div class="set-name">Preferred port</div><div class="set-hint" id="dashPortHint">loopback only; never network-facing</div></div>
619
- <div class="set-ctl" style="display:flex;gap:8px;align-items:center">
620
- <input id="dashPortInput" type="number" min="0" max="65535" step="1" class="choice" style="width:120px" inputmode="numeric" />
621
+ <div class="set-ctl ladd" style="align-items:center">
622
+ <input id="dashPortInput" type="number" min="0" max="65535" step="1" class="linput" style="flex:none;width:120px" inputmode="numeric" />
621
623
  <button id="dashPortSave" type="button" class="toggle on">Save</button>
622
624
  </div>
623
625
  </div>
@@ -1644,7 +1646,7 @@ export {
1644
1646
  function prettyType(k) { return TYPE_LABELS[k] || String(k).replace(/\b\w/g, (c) => c.toUpperCase()); }
1645
1647
 
1646
1648
  // One bar per key (source or type), sorted by tokens saved, with an optional $ column.
1647
- function renderBreakdown(el, map, label, money) {
1649
+ function renderBreakdown(el, map, label, money, iconFn) {
1648
1650
  const entries = Object.entries(map || {}).sort((a, b) => (b[1].tokensSaved || 0) - (a[1].tokensSaved || 0));
1649
1651
  if (!entries.length) {
1650
1652
  el.innerHTML = '<div class="empty" style="padding:20px 0">No data yet - this fills in as compressions are recorded.</div>';
@@ -1654,7 +1656,7 @@ export {
1654
1656
  el.innerHTML = entries.map(([k, v]) => {
1655
1657
  const saved = v.tokensSaved || 0, pct = Math.round(saved / max * 100);
1656
1658
  const m = money ? '<span class="money">' + fmtUsd(moneyOf(k, saved)) + '</span>' : '';
1657
- return '<div class="bar-row"><span class="name" style="width:140px">' + label(k)
1659
+ return '<div class="bar-row"><span class="name" style="width:140px">' + iconLabel(iconFn ? iconFn(k) : "", label(k))
1658
1660
  + '</span><div class="bar-track"><div class="bar-fill" style="width:' + pct + '%;background:var(--accent)"></div></div>'
1659
1661
  + '<span class="num">' + fmt(saved) + '</span>' + m
1660
1662
  + '<span class="foot" style="width:64px;text-align:right;margin:0">' + fmt(v.calls || 0) + ' calls</span></div>';
@@ -1676,8 +1678,9 @@ export {
1676
1678
  + '<th class="r">Before</th><th class="r">After</th><th class="r">Saved</th></tr></thead><tbody>'
1677
1679
  + rows.map((p) => {
1678
1680
  const saved = savedOf(p), pct = p.b ? Math.round(saved / p.b * 100) : 0;
1681
+ const src = p.s || "unknown";
1679
1682
  return '<tr><td>' + new Date(p.t).toLocaleString() + '</td><td>' + prettyType(p.c || "unknown")
1680
- + '</td><td>' + prettySource(p.s || "unknown") + '</td><td class="r">' + fmt(p.b || 0)
1683
+ + '</td><td>' + iconLabel(sourceSvg(src), prettySource(src)) + '</td><td class="r">' + fmt(p.b || 0)
1681
1684
  + '</td><td class="r">' + fmt(p.a || 0) + '</td><td class="r good">' + fmt(saved) + " (" + pct + "%)</td></tr>";
1682
1685
  }).join("") + "</tbody></table>";
1683
1686
  }
@@ -1851,10 +1854,17 @@ export {
1851
1854
  el.onclick = null; el.style.cursor = ""; el.title = ""; // clear any prior incident affordance
1852
1855
  let ind = "unknown", desc = p.label + " status unavailable";
1853
1856
  try {
1854
- const r = await fetch(p.status, { cache: "no-store" });
1855
- const d = await r.json();
1856
- ind = (d.status && d.status.indicator) || "unknown";
1857
- desc = (d.status && d.status.description) || "Status unknown";
1857
+ // Prefer the same-origin server proxy: the browser's direct cross-origin fetch is
1858
+ // unreliable in the field (blockers/MITM proxies/captive portals returned a stub
1859
+ // 200 -> a bogus "Status unknown"). Fall back to direct if the server predates it.
1860
+ let s = null;
1861
+ try {
1862
+ const rp = await fetch("/api/provider-status?provider=" + encodeURIComponent(usageProvider), { cache: "no-store" });
1863
+ if (rp.ok) { const dp = await rp.json(); if (!dp.error) s = dp; }
1864
+ } catch { /* fall through to the direct statuspage */ }
1865
+ if (!s) { const d = await (await fetch(p.status, { cache: "no-store" })).json(); s = d.status || {}; }
1866
+ ind = s.indicator || "unknown";
1867
+ desc = s.description || "Status unknown";
1858
1868
  } catch { ind = "unknown"; }
1859
1869
  // Claude Code only: overlay the community incident signal. It elevates an otherwise
1860
1870
  // green/unknown pill to a warning; an official major/critical statuspage state wins.
@@ -1992,6 +2002,7 @@ export {
1992
2002
  // measurable local store; the others show an honest "no local usage" panel instead.
1993
2003
  function applyUsageView() {
1994
2004
  const p = USAGE_PROVIDERS[usageProvider] || USAGE_PROVIDERS.claude;
2005
+ $("uProviderIcon").innerHTML = agentSvg(usageProvider);
1995
2006
  $("usageTitle").textContent = p.label + " Usage";
1996
2007
  const link = $("usageViewLink");
1997
2008
  link.href = p.viewHref; link.textContent = p.viewLabel + " ->";
@@ -2201,7 +2212,7 @@ export {
2201
2212
  $("removed").textContent = fmt(saved) + " (" + rate + "%)";
2202
2213
  $("reduction").textContent = before ? (100 - rate) + "% of input sent" : "-";
2203
2214
  $("ratio").textContent = after ? "~" + (before / after).toFixed(1) + ":1" : "-";
2204
- renderBreakdown($("sources"), s.bySource, prettySource, true);
2215
+ renderBreakdown($("sources"), s.bySource, prettySource, true, sourceSvg);
2205
2216
  renderBreakdown($("types"), s.byType, prettyType, false);
2206
2217
 
2207
2218
  renderUsage(data.usage);
@@ -2688,6 +2699,14 @@ export {
2688
2699
  const AGENT_NAME_BY_LABEL = { "Claude Code": "claude", "OpenAI Codex": "codex", "OpenCode": "opencode" };
2689
2700
  const agentSvg = (name) => AGENT_ICONS[name] || "";
2690
2701
 
2702
+ // Compression sources -> a brand icon. Reuses the app icons; the CLI source is enigma itself
2703
+ // (the monochrome cube mark, recoloured to currentColor so it sits uniformly in the tables).
2704
+ const ENIGMA_ICON = '<svg viewBox="0 0 2187.36 2500.37" fill="currentColor" aria-hidden="true"><path d="m2152.75 605.14c-2.56-1.46-5.13-2.93-8.06-4.76q-254.23-145.51-507.76-291.38-266.32-152.69-532.28-305.34l-6.23-3.66-6.59 2.56a202.53 202.53 0 0 0 -29.28 13.55q-231.73 135.63-463.1 270.89c-191.83 112.4-384.03 224.42-574.39 336.44-23.8 11-23.07 35.88-22.7 50.89 0 3.29.37 7 0 10.62q-.55 426.12-1.46 852.24l-.74 316.3c-1.46 19 7 35.15 21.24 41.74q330 187.24 660.05 375.23l389.15 221.54c8.79 5.49 17.57 8.42 25.63 8.42a29.12 29.12 0 0 0 18.3-6.23l564.47-325.13c160.35-92.26 320.7-184.88 479.94-276.4 17.94-8.05 28.56-26 26.36-42.83l1.83-1197.1c2.56-27.46-20.5-40.27-34.41-47.59m-1058.32 75.41 464.19 270.17-463.46 267.28-157.79-89.33q-155.94-88.41-311.9-176.45l469-271.64m527.52 233.57-504.1-293.24c-13.91-9.88-32.21-9.88-45.39-1.1l-510.73 295.77-463.83-262.12 323.62-189.26q336.62-197.15 673.6-393.55 309.15 178.47 618.68 355.47 188.91 108.74 377.8 217.09l-469.69 270.91m-1555.86-205.74 461.63 261.38 9.52 576.59c-1.1 17.93 7.69 34 21.6 40.27q153.19 88.38 307.19 176.45l197 113.12-.36 538.51-998.78-568.16zm525.7 297.62 471.88 266.88-.73 530.46-462.36-265.78zm534.85 870.56 197.32-113.12c101-58.21 202.45-116 304.22-174.63l2.93-1.83c5.49-3.29 12.81-7.69 17.93-15l2.93-4v-4.76c-1.09-127-1.09-256.63-.73-381.83 0-70.29.37-140.58 0-210.87l472.25-272.36-.73 464.56-1.1 678.36q-316.29 181.76-633 364.25l-362.79 209 .73-537.78m0-72.86.37-530.82 464.93-268v531.92q-232.29 132.89-465.3 266.87"/></svg>';
2705
+ const SOURCE_ICON_KEY = { "claude-code": "claude", "claude": "claude", "claude-desktop": "claude", "opencode": "opencode", "codex": "codex", "codex-cli": "codex" };
2706
+ function sourceSvg(k) { return k === "cli" ? ENIGMA_ICON : (AGENT_ICONS[SOURCE_ICON_KEY[k]] || ""); }
2707
+ // Brand icon + text in one inline tag; the icon is omitted when there is none (e.g. content types).
2708
+ function iconLabel(ic, text) { return '<span class="srctag">' + (ic ? '<span class="aicon">' + ic + '</span>' : '') + esc(text) + '</span>'; }
2709
+
2691
2710
  function skillRow(s) {
2692
2711
  const ver = s.version ? '<span class="tag">v' + esc(s.version) + "</span>" : "";
2693
2712
  // Last-edited date (from skill.json), shown relative via the vendored <relative-time>.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enigmax/dashboard",
3
- "version": "0.1.42",
3
+ "version": "0.1.43",
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": [