@enigmax/dashboard 0.1.12 → 0.1.14

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 +113 -19
  2. package/package.json +1 -1
package/assets/index.html CHANGED
@@ -8,40 +8,67 @@
8
8
  :root {
9
9
  --bg: #0b0e14; --surface: #151a23; --surface2: #1b2230; --border: #232a36;
10
10
  --text: #e6e6e6; --muted: #8a93a3; --accent: #e0a458; --accent2: #d7875f; --good: #6fcf97;
11
+ /* Type roles: a monospace "machine readout" face carries the brand, headings,
12
+ labels and numeric data (this is a CLI tool's dashboard); sans carries prose. */
13
+ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
14
+ --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
15
+ --ring: rgba(224,164,88,0.6);
11
16
  }
12
17
  * { box-sizing: border-box; }
13
18
  body {
14
19
  margin: 0; background: var(--bg); color: var(--text);
15
- font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
20
+ font-family: var(--sans);
16
21
  -webkit-font-smoothing: antialiased; padding: 24px; max-width: 2200px; margin: 0 auto;
17
22
  }
18
23
  a { color: var(--accent2); }
24
+ /* Quality floor: one visible keyboard-focus ring for every interactive control
25
+ (the custom-styled buttons/tabs/links carry no native outline otherwise). */
26
+ a:focus-visible, button:focus-visible, select:focus-visible,
27
+ input:focus-visible, [tabindex]:focus-visible {
28
+ outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 6px;
29
+ }
30
+ @media (prefers-reduced-motion: reduce) {
31
+ *, *::before, *::after {
32
+ animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
33
+ transition-duration: 0.01ms !important;
34
+ }
35
+ }
19
36
  header { display: flex; align-items: center; gap: 20px; margin-bottom: 24px; flex-wrap: wrap; }
20
- .brand { display: flex; flex-direction: column; }
21
- .wordmark { font-size: 20px; font-weight: 700; letter-spacing: 0.5px; }
22
- .wordmark span { color: var(--accent); }
23
- .sub { color: var(--muted); font-size: 13px; }
37
+ .brand { display: flex; flex-direction: column; gap: 3px; }
38
+ .wordmark {
39
+ font-family: var(--mono); font-size: 19px; font-weight: 600; letter-spacing: 0.5px;
40
+ display: inline-flex; align-items: center;
41
+ }
42
+ /* Signature: a blinking block caret turns the wordmark into a live terminal prompt. */
43
+ .wordmark span {
44
+ display: inline-block; width: 0.46em; height: 1.05em; margin-left: 4px;
45
+ background: var(--accent); transform: translateY(1px); text-indent: -9999px; overflow: hidden;
46
+ animation: caret 1.1s steps(1, end) infinite;
47
+ }
48
+ @keyframes caret { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }
49
+ .sub { font-family: var(--mono); color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 1.4px; }
24
50
  .tabs { display: flex; gap: 4px; }
25
51
  .tabs .tab {
26
- text-decoration: none; color: var(--muted); font-size: 13px; font-weight: 600;
27
- padding: 7px 16px; border-radius: 8px; border: 1px solid transparent; transition: background .15s, color .15s, border-color .15s;
52
+ font-family: var(--mono); text-decoration: none; color: var(--muted); font-size: 12px; font-weight: 600;
53
+ padding: 7px 14px; border-radius: 8px; border: 1px solid transparent; transition: background .15s, color .15s, border-color .15s;
28
54
  }
29
55
  .tabs .tab:hover { color: var(--text); background: var(--surface); }
30
56
  .tabs .tab.active { color: var(--accent); background: var(--surface); border-color: var(--border); }
31
- .live { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 12px; }
57
+ .live { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 12px; font-family: var(--mono); }
32
58
  .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); box-shadow: 0 0 8px var(--good); }
33
59
  .dot.stale { background: var(--muted); box-shadow: none; }
34
60
  .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 20px; }
35
- .card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 16px; }
36
- .card .label { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.6px; }
37
- .card .value { font-size: 28px; font-weight: 700; margin-top: 6px; }
61
+ .card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 16px; transition: border-color .15s; }
62
+ .card:hover { border-color: var(--surface2); }
63
+ .card .label { font-family: var(--mono); color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.7px; }
64
+ .card .value { font-family: var(--mono); font-size: 28px; font-weight: 600; margin-top: 8px; font-variant-numeric: tabular-nums; letter-spacing: -0.5px; }
38
65
  .card .value.accent { color: var(--accent); }
39
66
  .card .value.good { color: var(--good); }
40
67
  .card .foot { color: var(--muted); font-size: 12px; margin-top: 4px; }
41
68
  .panel { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 18px; margin-bottom: 20px; }
42
69
  .panel-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
43
- .panel h2 { font-size: 14px; font-weight: 600; margin: 0; color: var(--text); }
44
- .panel h2 small { color: var(--muted); font-weight: 400; margin-left: 8px; }
70
+ .panel h2 { font-family: var(--mono); font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.8px; margin: 0; color: var(--text); }
71
+ .panel h2 small { font-family: var(--sans); color: var(--muted); font-weight: 400; font-size: 12px; text-transform: none; letter-spacing: 0; margin-left: 8px; }
45
72
  .ctrls { margin-left: auto; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
46
73
  .ranges { display: flex; gap: 4px; }
47
74
  .ranges button {
@@ -85,11 +112,11 @@
85
112
  footer { color: var(--muted); font-size: 12px; line-height: 1.6; margin-top: 8px; }
86
113
  code { background: var(--surface2); padding: 1px 6px; border-radius: 5px; color: var(--accent2); }
87
114
  .page-head { margin-bottom: 18px; }
88
- .page-head h1 { font-size: 18px; font-weight: 700; margin: 0 0 4px; }
115
+ .page-head h1 { font-family: var(--mono); font-size: 18px; font-weight: 600; letter-spacing: 0.3px; margin: 0 0 4px; }
89
116
  .page-head p { color: var(--muted); font-size: 13px; margin: 0; max-width: 720px; line-height: 1.5; }
90
117
  .set-cat { margin-bottom: 18px; }
91
118
  .set-cat:last-child { margin-bottom: 0; }
92
- .set-cat-h { color: var(--accent); font-size: 12px; text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 4px; }
119
+ .set-cat-h { font-family: var(--mono); color: var(--accent); font-size: 12px; text-transform: uppercase; letter-spacing: 0.7px; margin-bottom: 4px; }
93
120
  .set-cat-b { color: var(--muted); font-size: 12px; margin-bottom: 8px; }
94
121
  .set-row { display: flex; align-items: center; gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--surface2); }
95
122
  .set-row:last-child { border-bottom: none; }
@@ -150,6 +177,22 @@
150
177
  .wval.muted { font-size: 14px; font-weight: 500; color: var(--muted); }
151
178
  .wset { display: flex; gap: 6px; margin-top: 10px; }
152
179
  .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; }
180
+ /* Fix-path result: per-tool rows with a status dot, badge, reason and resolved path. */
181
+ .fixres { margin-top: 12px; display: none; }
182
+ .fixres.show { display: block; }
183
+ .fixres-head { font-size: 13px; font-weight: 600; margin-bottom: 4px; }
184
+ .fixres-head.ok { color: var(--good); }
185
+ .fixres-head.fixed { color: var(--accent); }
186
+ .fixres-head.bad { color: var(--accent2); }
187
+ .fxrow { display: flex; align-items: flex-start; gap: 10px; padding: 9px 0; border-top: 1px solid var(--surface2); }
188
+ .fxdot { width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; flex: 0 0 auto; }
189
+ .fxdot.ok { background: var(--good); }
190
+ .fxdot.fixed { background: var(--accent); }
191
+ .fxdot.bad { background: var(--accent2); }
192
+ .fxmain { min-width: 0; flex: 1; }
193
+ .fxtitle { font-size: 13px; display: flex; align-items: center; gap: 8px; }
194
+ .fxmsg { color: var(--muted); font-size: 12px; margin-top: 2px; }
195
+ .fxpath { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; color: var(--muted); margin-top: 3px; word-break: break-all; }
153
196
  .sys-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 0 24px; }
154
197
  .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; }
155
198
  .sys-item .k { color: var(--muted); }
@@ -157,13 +200,20 @@
157
200
  .pill.on { color: #06210f; background: var(--good); border-color: var(--good); font-weight: 600; }
158
201
  .pill.off { color: var(--muted); background: var(--surface2); }
159
202
  .pill.lvl { color: #1a1206; background: var(--accent); border-color: var(--accent); font-weight: 600; }
160
- @media (max-width: 720px) { .grid { grid-template-columns: repeat(2, 1fr); } header { gap: 12px; } }
203
+ @media (max-width: 720px) {
204
+ body { padding: 16px; }
205
+ .grid { grid-template-columns: repeat(2, 1fr); }
206
+ header { gap: 12px; }
207
+ .tabs { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
208
+ .tabs .tab { white-space: nowrap; }
209
+ .nav { margin-left: 0; }
210
+ }
161
211
  </style>
162
212
  </head>
163
213
  <body>
164
214
  <header>
165
215
  <div class="brand">
166
- <div class="wordmark">Enigma<span>.</span></div>
216
+ <div class="wordmark">Enigma<span aria-hidden="true">.</span></div>
167
217
  <div class="sub">Savings dashboard</div>
168
218
  </div>
169
219
  <nav class="tabs">
@@ -212,6 +262,7 @@
212
262
  <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>
213
263
  <div id="systems" class="sys-grid"><div class="empty" style="padding:16px 0">Loading...</div></div>
214
264
  <div id="updateNote" class="set-note"></div>
265
+ <div id="fixPathResult" class="fixres"></div>
215
266
  </div>
216
267
 
217
268
  <div class="panel">
@@ -1039,13 +1090,56 @@
1039
1090
  // Detect & repair every tool's launch path; the server persists any off-PATH
1040
1091
  // binary so `enigma <tool>` works, and returns the refreshed status.
1041
1092
  async function runFixPath() {
1093
+ const btn = $("fixPathBtn");
1094
+ $("fixPathResult").className = "fixres";
1042
1095
  $("updateNote").textContent = "Detecting tool paths...";
1096
+ if (btn) { btn.disabled = true; }
1043
1097
  try {
1044
1098
  const res = await fetch("/api/fix-path", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" });
1045
1099
  const out = await res.json();
1046
- $("updateNote").textContent = (out.lines || []).join(" ") || (out.ok ? "Tool paths OK." : "Could not fix all tools.");
1100
+ $("updateNote").textContent = "";
1101
+ renderFixPath(out);
1047
1102
  loadSystems(true);
1048
- } catch { $("updateNote").textContent = "Could not reach the server to fix tool paths."; }
1103
+ } catch {
1104
+ $("updateNote").textContent = "";
1105
+ $("fixPathResult").className = "fixres show";
1106
+ $("fixPathResult").innerHTML = '<div class="fixres-head bad">Could not reach the server to fix tool paths.</div>';
1107
+ } finally { if (btn) { btn.disabled = false; } }
1108
+ }
1109
+
1110
+ // Short, non-redundant reason per tool (the resolved path is shown separately below).
1111
+ function fixReason(r) {
1112
+ if (!r.installed) return "Not installed. Install it, then run fix-path again.";
1113
+ if (r.changed) return "Found off PATH and saved - 'enigma " + esc(r.command) + "' now launches it.";
1114
+ return "Launchable - '" + esc(r.command) + "' works.";
1115
+ }
1116
+
1117
+ // Render the fix-path outcome as one row per tool: status dot + badge + reason + path.
1118
+ function renderFixPath(out) {
1119
+ const box = $("fixPathResult");
1120
+ const results = out.results || [];
1121
+ box.className = "fixres show";
1122
+ if (!results.length) {
1123
+ box.innerHTML = '<div class="fxmsg">' + esc((out.lines || []).join(" ") || (out.ok ? "Tool paths OK." : "Could not fix tools.")) + "</div>";
1124
+ return;
1125
+ }
1126
+ const fixedN = results.filter((r) => r.changed).length;
1127
+ const badN = results.filter((r) => !r.ok).length;
1128
+ let head, hc;
1129
+ if (badN) { head = badN + " of " + results.length + " tools need attention"; hc = "bad"; }
1130
+ else if (fixedN) { head = "Repaired " + fixedN + " of " + results.length + " tools - all launchable now"; hc = "fixed"; }
1131
+ else { head = "All " + results.length + " tools launchable"; hc = "ok"; }
1132
+ const rows = results.map((r) => {
1133
+ const cls = !r.ok ? "bad" : (r.changed ? "fixed" : "ok");
1134
+ const badge = !r.ok ? '<span class="pill off">not found</span>'
1135
+ : (r.changed ? '<span class="pill lvl">fixed</span>' : '<span class="pill on">ok</span>');
1136
+ return '<div class="fxrow"><span class="fxdot ' + cls + '"></span><div class="fxmain">'
1137
+ + '<div class="fxtitle">' + esc(r.label) + " " + badge + "</div>"
1138
+ + '<div class="fxmsg">' + fixReason(r) + "</div>"
1139
+ + (r.path ? '<div class="fxpath">' + esc(r.path) + "</div>" : "")
1140
+ + "</div></div>";
1141
+ }).join("");
1142
+ box.innerHTML = '<div class="fixres-head ' + hc + '">' + esc(head) + "</div>" + rows;
1049
1143
  }
1050
1144
 
1051
1145
  async function loadSystems(force) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enigmax/dashboard",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
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": [