@enigmax/dashboard 0.1.4 → 0.1.6
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 +53 -20
- 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:
|
|
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,12 +108,18 @@
|
|
|
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;
|
|
114
118
|
}
|
|
115
119
|
.tag.ext { color: var(--accent2); }
|
|
116
120
|
.tag.warn { color: var(--accent); border-color: var(--accent); }
|
|
121
|
+
.tag.exp { color: var(--accent); border-color: var(--accent); background: rgba(224,164,88,0.10); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; }
|
|
122
|
+
.tag.meas { color: var(--good); border-color: var(--good); background: rgba(111,207,151,0.10); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; }
|
|
117
123
|
.editor {
|
|
118
124
|
width: 100%; min-height: 360px; resize: vertical; background: var(--surface2); color: var(--text);
|
|
119
125
|
border: 1px solid var(--border); border-radius: 8px; padding: 12px;
|
|
@@ -175,7 +181,10 @@
|
|
|
175
181
|
<div class="panel">
|
|
176
182
|
<div class="panel-head">
|
|
177
183
|
<h2>Enigma Systems <small>what's active - configuration, not savings</small></h2>
|
|
178
|
-
<div class="ctrls"
|
|
184
|
+
<div class="ctrls">
|
|
185
|
+
<button id="refreshBtn" type="button" class="toggle">Refresh</button>
|
|
186
|
+
<button id="updateBtn" type="button" class="toggle on">Check & update</button>
|
|
187
|
+
</div>
|
|
179
188
|
</div>
|
|
180
189
|
<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>
|
|
181
190
|
<div id="systems" class="sys-grid"><div class="empty" style="padding:16px 0">Loading...</div></div>
|
|
@@ -293,11 +302,9 @@
|
|
|
293
302
|
<div class="page-head">
|
|
294
303
|
<h1>Settings</h1>
|
|
295
304
|
<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>
|
|
296
|
-
</div>
|
|
297
|
-
<div class="panel">
|
|
298
|
-
<div id="settingsBody"><div class="empty" style="padding:24px 0">Loading settings...</div></div>
|
|
299
305
|
<div id="settingsNote" class="set-note"></div>
|
|
300
306
|
</div>
|
|
307
|
+
<div id="settingsBody"><div class="empty" style="padding:24px 0">Loading settings...</div></div>
|
|
301
308
|
</section>
|
|
302
309
|
|
|
303
310
|
<footer>
|
|
@@ -644,9 +651,14 @@
|
|
|
644
651
|
$("updated").textContent = "Updated " + new Date(data.generatedAt || Date.now()).toLocaleTimeString();
|
|
645
652
|
}
|
|
646
653
|
|
|
647
|
-
|
|
648
|
-
|
|
654
|
+
// Auto-refresh is on by default while the tab is focused; `live` mirrors the
|
|
655
|
+
// dashboard-live config (read from /api/status). A forced refresh (button / view
|
|
656
|
+
// change) always runs so the user can refresh even with auto-refresh off.
|
|
657
|
+
let live = true;
|
|
658
|
+
async function poll(force) {
|
|
659
|
+
// Never hit the server while the tab is hidden, or when auto-refresh is off (unless forced).
|
|
649
660
|
if (document.hidden) return;
|
|
661
|
+
if (!live && !force) return;
|
|
650
662
|
try {
|
|
651
663
|
const res = await fetch("/api/stats", { cache: "no-store" });
|
|
652
664
|
render(await res.json());
|
|
@@ -659,6 +671,15 @@
|
|
|
659
671
|
// --- settings subpage (mirrors the TUI registry over /api/settings) ---
|
|
660
672
|
function esc(s) { return String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c])); }
|
|
661
673
|
|
|
674
|
+
// Turn "(experimental)" and " · measured/experimental" label suffixes into visual badges.
|
|
675
|
+
function labelWithBadges(text) {
|
|
676
|
+
let name = String(text);
|
|
677
|
+
const badges = [];
|
|
678
|
+
if (/\(experimental\)/i.test(name)) { name = name.replace(/\s*\(experimental\)/i, ""); badges.push(["experimental", "exp"]); }
|
|
679
|
+
name = name.replace(/\s*·\s*(measured|experimental)\b/gi, (_, w) => { badges.push([w.toLowerCase(), w.toLowerCase() === "experimental" ? "exp" : "meas"]); return ""; });
|
|
680
|
+
return esc(name) + badges.map(([t, c]) => ' <span class="tag ' + c + '">' + esc(t) + "</span>").join("");
|
|
681
|
+
}
|
|
682
|
+
|
|
662
683
|
function settingRow(s) {
|
|
663
684
|
const ctl = s.choices
|
|
664
685
|
? '<select class="choice" data-skey="' + esc(s.key) + '">'
|
|
@@ -666,7 +687,7 @@
|
|
|
666
687
|
+ "</select>"
|
|
667
688
|
: '<button type="button" class="toggle' + (s.value ? " on" : "") + '" data-skey="' + esc(s.key)
|
|
668
689
|
+ '" data-kind="bool" data-on="' + (s.value ? 1 : 0) + '">' + (s.value ? "On" : "Off") + "</button>";
|
|
669
|
-
return '<div class="set-row"><div class="set-meta"><div class="set-name">' +
|
|
690
|
+
return '<div class="set-row"><div class="set-meta"><div class="set-name">' + labelWithBadges(s.label) + "</div>"
|
|
670
691
|
+ '<div class="set-hint">' + esc(s.hint) + (s.globalOnly ? " · global" : "") + "</div></div>"
|
|
671
692
|
+ '<div class="set-ctl">' + ctl + "</div></div>";
|
|
672
693
|
}
|
|
@@ -674,9 +695,11 @@
|
|
|
674
695
|
function renderSettings(cats) {
|
|
675
696
|
const el = $("settingsBody");
|
|
676
697
|
if (!cats.length) { el.innerHTML = '<div class="empty" style="padding:24px 0">No settings available.</div>'; return; }
|
|
677
|
-
|
|
698
|
+
// One panel per category; settings laid out in a responsive 2-column grid so a
|
|
699
|
+
// long config list stays scannable on a wide screen.
|
|
700
|
+
el.innerHTML = cats.map((c) => '<div class="panel"><div class="set-cat-h">' + esc(c.title) + "</div>"
|
|
678
701
|
+ (c.blurb ? '<div class="set-cat-b">' + esc(c.blurb) + "</div>" : "")
|
|
679
|
-
+ c.settings.map(settingRow).join("") + "</div>").join("");
|
|
702
|
+
+ '<div class="set-grid">' + c.settings.map(settingRow).join("") + "</div></div>").join("");
|
|
680
703
|
}
|
|
681
704
|
|
|
682
705
|
async function postSetting(key, value, ctl) {
|
|
@@ -720,7 +743,7 @@
|
|
|
720
743
|
function renderSystems(s) {
|
|
721
744
|
const el = $("systems");
|
|
722
745
|
if (!s) { el.innerHTML = '<div class="empty" style="padding:16px 0">Status unavailable.</div>'; return; }
|
|
723
|
-
const item = (k, v) => '<div class="sys-item"><span class="k">' + k + '</span><span class="v">' + v + "</span></div>";
|
|
746
|
+
const item = (k, v) => '<div class="sys-item"><span class="k">' + labelWithBadges(k) + '</span><span class="v">' + v + "</span></div>";
|
|
724
747
|
const sk = s.skills || {};
|
|
725
748
|
const skillTags = '<span class="tag">' + (sk.enigma || 0) + " enigma</span>"
|
|
726
749
|
+ '<span class="tag ext">' + (sk.external || 0) + " external</span>"
|
|
@@ -753,19 +776,28 @@
|
|
|
753
776
|
try {
|
|
754
777
|
const res = await fetch("/api/update", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" });
|
|
755
778
|
const out = await res.json();
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
779
|
+
if (!out.ok) { $("updateNote").textContent = "Update failed: " + (out.error || "error"); return; }
|
|
780
|
+
// The UI bundle may have been refreshed server-side; reload so the browser
|
|
781
|
+
// picks up the new dashboard and fresh data automatically.
|
|
782
|
+
$("updateNote").textContent = (out.note || "Updated.") + " Refreshing the dashboard...";
|
|
783
|
+
setTimeout(() => location.reload(), 1200);
|
|
759
784
|
} catch { $("updateNote").textContent = "Could not reach the server to update."; }
|
|
760
785
|
}
|
|
761
786
|
|
|
762
|
-
async function loadSystems() {
|
|
787
|
+
async function loadSystems(force) {
|
|
788
|
+
if (!live && !force) return;
|
|
763
789
|
try {
|
|
764
790
|
const r = await fetch("/api/status", { cache: "no-store" });
|
|
765
|
-
|
|
791
|
+
const sys = (await r.json()).systems;
|
|
792
|
+
if (sys) live = sys.live !== false; // pick up the dashboard-live config
|
|
793
|
+
renderSystems(sys);
|
|
766
794
|
} catch { $("systems").innerHTML = '<div class="empty" style="padding:16px 0">Status unavailable.</div>'; }
|
|
767
795
|
}
|
|
768
796
|
|
|
797
|
+
function onSavings() { const h = (location.hash || "").replace(/^#\/?/, ""); return h !== "settings" && h !== "skills"; }
|
|
798
|
+
// One refresh of everything visible on the Savings view; `force` ignores auto-refresh-off.
|
|
799
|
+
function refreshAll(force) { poll(force); if (onSavings()) loadSystems(force); }
|
|
800
|
+
|
|
769
801
|
// --- skills subpage (lists enigma + external skills over /api/skills) ---
|
|
770
802
|
function skillRow(s) {
|
|
771
803
|
const ver = s.version ? '<span class="tag">v' + esc(s.version) + "</span>" : "";
|
|
@@ -871,16 +903,17 @@
|
|
|
871
903
|
if (v === "settings" && !settingsLoaded) { settingsLoaded = true; loadSettings(); }
|
|
872
904
|
if (v === "skills" && !skillsLoaded) { skillsLoaded = true; loadSkills(); }
|
|
873
905
|
// The chart was sized while its view may have been hidden; nudge it on return.
|
|
874
|
-
if (v === "savings") { loadSystems(); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
|
|
906
|
+
if (v === "savings") { loadSystems(true); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
|
|
875
907
|
}
|
|
876
908
|
|
|
877
909
|
initChart();
|
|
878
|
-
poll();
|
|
879
|
-
setInterval(
|
|
880
|
-
document.addEventListener("visibilitychange", () => { if (!document.hidden)
|
|
910
|
+
poll(true);
|
|
911
|
+
setInterval(() => refreshAll(false), POLL_MS);
|
|
912
|
+
document.addEventListener("visibilitychange", () => { if (!document.hidden) refreshAll(true); });
|
|
881
913
|
wireSettings();
|
|
882
914
|
wireSkills();
|
|
883
915
|
$("updateBtn").addEventListener("click", runUpdate);
|
|
916
|
+
$("refreshBtn").addEventListener("click", () => refreshAll(true));
|
|
884
917
|
window.addEventListener("hashchange", route);
|
|
885
918
|
route();
|
|
886
919
|
</script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enigmax/dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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": [
|