@enigmax/dashboard 0.1.3 → 0.1.5

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 +62 -15
  2. package/package.json +1 -1
package/assets/index.html CHANGED
@@ -13,7 +13,7 @@
13
13
  body {
14
14
  margin: 0; background: var(--bg); color: var(--text);
15
15
  font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
16
- -webkit-font-smoothing: antialiased; padding: 24px; max-width: 1040px; margin: 0 auto;
16
+ -webkit-font-smoothing: antialiased; padding: 24px; max-width: 2200px; margin: 0 auto;
17
17
  }
18
18
  a { color: var(--accent2); }
19
19
  header { display: flex; align-items: center; gap: 20px; margin-bottom: 24px; flex-wrap: wrap; }
@@ -108,6 +108,10 @@
108
108
  border-radius: 7px; padding: 5px 10px; font-size: 12px; font-family: inherit; cursor: pointer;
109
109
  }
110
110
  .set-note { color: var(--accent); font-size: 12px; min-height: 16px; margin-top: 10px; }
111
+ /* Settings: two-column grid of rows on wide screens, so a long config list stays compact. */
112
+ .set-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: 0 32px; }
113
+ .set-grid .set-row { padding: 10px 0; }
114
+ .panel > .set-cat-h { margin-top: 0; }
111
115
  .tag {
112
116
  display: inline-block; font-size: 11px; color: var(--muted); background: var(--surface2);
113
117
  border: 1px solid var(--border); border-radius: 6px; padding: 1px 7px; margin-left: 6px; vertical-align: middle;
@@ -173,9 +177,16 @@
173
177
  </div>
174
178
 
175
179
  <div class="panel">
176
- <h2 style="margin-bottom:4px">Enigma Systems <small>what's active - configuration, not savings</small></h2>
180
+ <div class="panel-head">
181
+ <h2>Enigma Systems <small>what's active - configuration, not savings</small></h2>
182
+ <div class="ctrls">
183
+ <button id="refreshBtn" type="button" class="toggle">Refresh</button>
184
+ <button id="updateBtn" type="button" class="toggle on">Check &amp; update</button>
185
+ </div>
186
+ </div>
177
187
  <div class="sub" style="margin-bottom:14px">Only Context Compression and (opt-in) real tool-usage are measured for savings below. The rest is shown as state: enigma can't measure their effect because they run inside the agent, not through enigma - so it reports their configuration honestly instead of inventing a number.</div>
178
188
  <div id="systems" class="sys-grid"><div class="empty" style="padding:16px 0">Loading...</div></div>
189
+ <div id="updateNote" class="set-note"></div>
179
190
  </div>
180
191
 
181
192
  <div class="panel">
@@ -289,11 +300,9 @@
289
300
  <div class="page-head">
290
301
  <h1>Settings</h1>
291
302
  <p>Everything you can configure with <code>enigma config</code> or the terminal UI, editable here. Changes apply at global scope and take effect immediately; toggles that change agent memory need an agent restart.</p>
292
- </div>
293
- <div class="panel">
294
- <div id="settingsBody"><div class="empty" style="padding:24px 0">Loading settings...</div></div>
295
303
  <div id="settingsNote" class="set-note"></div>
296
304
  </div>
305
+ <div id="settingsBody"><div class="empty" style="padding:24px 0">Loading settings...</div></div>
297
306
  </section>
298
307
 
299
308
  <footer>
@@ -640,9 +649,14 @@
640
649
  $("updated").textContent = "Updated " + new Date(data.generatedAt || Date.now()).toLocaleTimeString();
641
650
  }
642
651
 
643
- async function poll() {
644
- // Headroom pattern: never hit the server while the tab is hidden.
652
+ // Auto-refresh is on by default while the tab is focused; `live` mirrors the
653
+ // dashboard-live config (read from /api/status). A forced refresh (button / view
654
+ // change) always runs so the user can refresh even with auto-refresh off.
655
+ let live = true;
656
+ async function poll(force) {
657
+ // Never hit the server while the tab is hidden, or when auto-refresh is off (unless forced).
645
658
  if (document.hidden) return;
659
+ if (!live && !force) return;
646
660
  try {
647
661
  const res = await fetch("/api/stats", { cache: "no-store" });
648
662
  render(await res.json());
@@ -670,9 +684,11 @@
670
684
  function renderSettings(cats) {
671
685
  const el = $("settingsBody");
672
686
  if (!cats.length) { el.innerHTML = '<div class="empty" style="padding:24px 0">No settings available.</div>'; return; }
673
- el.innerHTML = cats.map((c) => '<div class="set-cat"><div class="set-cat-h">' + esc(c.title) + "</div>"
687
+ // One panel per category; settings laid out in a responsive 2-column grid so a
688
+ // long config list stays scannable on a wide screen.
689
+ el.innerHTML = cats.map((c) => '<div class="panel"><div class="set-cat-h">' + esc(c.title) + "</div>"
674
690
  + (c.blurb ? '<div class="set-cat-b">' + esc(c.blurb) + "</div>" : "")
675
- + c.settings.map(settingRow).join("") + "</div>").join("");
691
+ + '<div class="set-grid">' + c.settings.map(settingRow).join("") + "</div></div>").join("");
676
692
  }
677
693
 
678
694
  async function postSetting(key, value, ctl) {
@@ -721,25 +737,54 @@
721
737
  const skillTags = '<span class="tag">' + (sk.enigma || 0) + " enigma</span>"
722
738
  + '<span class="tag ext">' + (sk.external || 0) + " external</span>"
723
739
  + ((sk.disabled || 0) ? '<span class="tag">' + sk.disabled + " disabled</span>" : "");
740
+ const sec = s.security || {};
741
+ const ps = s.proxyStats || {};
742
+ const proxyMeasured = (ps.calls || 0) > 0
743
+ ? item("Proxy measured · real", '<span class="tag">' + fmt(ps.calls) + " calls</span><span class=\"tag\">"
744
+ + fmt(ps.input) + " in</span><span class=\"tag\">" + fmt(ps.output) + " out</span><span class=\"tag\">" + fmt(ps.cacheRead) + " cache</span>")
745
+ : "";
746
+ const guard = (sec.guardProtects || []).map((p) => '<span class="tag">' + esc(p) + "</span>").join("");
724
747
  el.innerHTML =
725
748
  item("Context compression (MCP) · measured", boolPill(s.compress))
726
749
  + item("Real tool-usage stats · measured", boolPill(s.usageStats))
750
+ + item("Claude Code measuring proxy · experimental", boolPill(s.proxy))
751
+ + proxyMeasured
727
752
  + item("Token-efficient output", levelPill(s.outputStyle))
728
753
  + item("Minimal code", levelPill(s.minimalCode))
729
754
  + item("Parallel sub-agents", boolPill(s.parallelSubagents))
730
755
  + item("Auto-lint on edit", boolPill(s.autoLint))
731
756
  + item("Commit emoji", boolPill(s.commitEmoji))
732
757
  + item("Local dashboard", pill(s.dashboard === "off" ? "off" : "lvl", s.dashboard || "off"))
758
+ + item("Permission bypass (security)", boolPill(sec.permissionBypass))
759
+ + item("Commit guard blocks", guard || '<span class="tag">set up with enigma security</span>')
733
760
  + item("Skills", skillTags);
734
761
  }
735
762
 
736
- async function loadSystems() {
763
+ async function runUpdate() {
764
+ $("updateNote").textContent = "Checking for updates...";
765
+ try {
766
+ const res = await fetch("/api/update", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" });
767
+ const out = await res.json();
768
+ $("updateNote").textContent = out.ok ? (out.note || "Updated.") : ("Update failed: " + (out.error || "error"));
769
+ loadSystems();
770
+ if (skillsLoaded) loadSkills();
771
+ } catch { $("updateNote").textContent = "Could not reach the server to update."; }
772
+ }
773
+
774
+ async function loadSystems(force) {
775
+ if (!live && !force) return;
737
776
  try {
738
777
  const r = await fetch("/api/status", { cache: "no-store" });
739
- renderSystems((await r.json()).systems);
778
+ const sys = (await r.json()).systems;
779
+ if (sys) live = sys.live !== false; // pick up the dashboard-live config
780
+ renderSystems(sys);
740
781
  } catch { $("systems").innerHTML = '<div class="empty" style="padding:16px 0">Status unavailable.</div>'; }
741
782
  }
742
783
 
784
+ function onSavings() { const h = (location.hash || "").replace(/^#\/?/, ""); return h !== "settings" && h !== "skills"; }
785
+ // One refresh of everything visible on the Savings view; `force` ignores auto-refresh-off.
786
+ function refreshAll(force) { poll(force); if (onSavings()) loadSystems(force); }
787
+
743
788
  // --- skills subpage (lists enigma + external skills over /api/skills) ---
744
789
  function skillRow(s) {
745
790
  const ver = s.version ? '<span class="tag">v' + esc(s.version) + "</span>" : "";
@@ -845,15 +890,17 @@
845
890
  if (v === "settings" && !settingsLoaded) { settingsLoaded = true; loadSettings(); }
846
891
  if (v === "skills" && !skillsLoaded) { skillsLoaded = true; loadSkills(); }
847
892
  // The chart was sized while its view may have been hidden; nudge it on return.
848
- if (v === "savings") { loadSystems(); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
893
+ if (v === "savings") { loadSystems(true); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
849
894
  }
850
895
 
851
896
  initChart();
852
- poll();
853
- setInterval(poll, POLL_MS);
854
- document.addEventListener("visibilitychange", () => { if (!document.hidden) poll(); });
897
+ poll(true);
898
+ setInterval(() => refreshAll(false), POLL_MS);
899
+ document.addEventListener("visibilitychange", () => { if (!document.hidden) refreshAll(true); });
855
900
  wireSettings();
856
901
  wireSkills();
902
+ $("updateBtn").addEventListener("click", runUpdate);
903
+ $("refreshBtn").addEventListener("click", () => refreshAll(true));
857
904
  window.addEventListener("hashchange", route);
858
905
  route();
859
906
  </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enigmax/dashboard",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
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": [