@enigmax/dashboard 0.1.28 → 0.1.30

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 +35 -9
  2. package/package.json +1 -1
package/assets/index.html CHANGED
@@ -159,8 +159,15 @@
159
159
  .toggle {
160
160
  background: var(--surface2); color: var(--muted); border: 1px solid var(--border);
161
161
  border-radius: var(--radius-sm); padding: 6px 16px; font-size: 12px; cursor: pointer; font-family: var(--mono); min-width: 56px; text-decoration: none;
162
- transition: color .15s, background .15s, border-color .15s;
163
- }
162
+ transition: color .15s, background .15s, border-color .15s, transform .1s;
163
+ }
164
+ /* Click feedback so every button visibly reacts: a quick press, plus a spin while
165
+ an icon action (Refresh) is actually running, and icon+label for import/export. */
166
+ .toggle:active, .iconbtn:active, .btn-danger:active, .achip:active { transform: scale(0.93); }
167
+ @keyframes spin { to { transform: rotate(360deg); } }
168
+ .iconbtn.spinning svg { animation: spin .7s linear infinite; }
169
+ .toggle.with-icon { display: inline-flex; align-items: center; gap: 6px; }
170
+ .toggle.with-icon svg { width: 14px; height: 14px; display: block; }
164
171
  .toggle:hover { color: var(--text); border-color: #2f3744; }
165
172
  .toggle.on { background: var(--good); color: #06210f; border-color: var(--good); font-weight: 600; }
166
173
  /* Boolean settings render as a sliding switch. Only the bool controls carry
@@ -182,7 +189,7 @@
182
189
  .toggle[data-kind="bool"].on { background: var(--good); border-color: var(--good); }
183
190
  .toggle[data-kind="bool"].on::after { left: 23px; background: #06210f; }
184
191
  /* Compact icon-only button (e.g. Edit), instead of a text button. */
185
- .iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 30px; padding: 0; background: var(--surface2); color: var(--muted); border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; transition: color .15s, border-color .15s; }
192
+ .iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 30px; padding: 0; background: var(--surface2); color: var(--muted); border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; transition: color .15s, border-color .15s, transform .1s; }
186
193
  .iconbtn:hover { color: var(--accent); border-color: #2f3744; }
187
194
  .iconbtn svg { width: 15px; height: 15px; display: block; }
188
195
  select.choice {
@@ -225,6 +232,7 @@
225
232
  .aicons { display: inline-flex; gap: 5px; align-items: center; vertical-align: middle; }
226
233
  .aicon { display: inline-flex; align-items: center; color: var(--muted); }
227
234
  .aicon svg { width: 14px; height: 14px; display: block; }
235
+ .set-ic { vertical-align: middle; margin-right: 7px; }
228
236
  /* List settings (guard block/allow globs, custom secret patterns): chips + add input. A list row spans the full grid width. */
229
237
  .set-row.list { grid-column: 1 / -1; align-items: stretch; flex-direction: column; }
230
238
  .lchips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
@@ -537,8 +545,8 @@
537
545
  <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>
538
546
  </div>
539
547
  <div style="display:flex;gap:8px;white-space:nowrap">
540
- <button id="cfgExport" type="button" class="toggle">Export config</button>
541
- <button id="cfgImport" type="button" class="toggle">Import config</button>
548
+ <button id="cfgExport" type="button" class="toggle with-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>Export config</button>
549
+ <button id="cfgImport" type="button" class="toggle with-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>Import config</button>
542
550
  </div>
543
551
  </div>
544
552
  <div id="settingsNote" class="set-note"></div>
@@ -2094,6 +2102,17 @@ export {
2094
2102
  return esc(name) + badges.map(([t, c]) => ' <span class="tag ' + c + '">' + esc(t) + "</span>").join("");
2095
2103
  }
2096
2104
 
2105
+ // App-specific settings (per-agent approval bypass) get the app's logo prefixed to
2106
+ // the label, so the Permissions list reads at a glance. AGENT_ICONS is defined below
2107
+ // but only referenced at render time, so it is initialised by then.
2108
+ const AGENT_LABEL_BY_NAME = { claude: "Claude Code", codex: "OpenAI Codex", opencode: "OpenCode" };
2109
+ function settingIcon(key) {
2110
+ const m = /^bypass-(claude|codex|opencode)$/.exec(key || "");
2111
+ if (!m) return "";
2112
+ const ic = (typeof AGENT_ICONS !== "undefined" && AGENT_ICONS[m[1]]) || "";
2113
+ return ic ? '<span class="aicon set-ic" title="' + esc(AGENT_LABEL_BY_NAME[m[1]]) + '" aria-hidden="true">' + ic + "</span>" : "";
2114
+ }
2115
+
2097
2116
  function settingRow(s) {
2098
2117
  if (s.kind === "list") {
2099
2118
  const items = s.items || [];
@@ -2104,7 +2123,7 @@ export {
2104
2123
  const adder = '<div class="ladd"><input type="text" class="linput" data-skey="' + esc(s.key)
2105
2124
  + '" placeholder="' + esc(s.itemHint || "add an entry...") + '">'
2106
2125
  + '<button type="button" class="laddb" data-skey="' + esc(s.key) + '">Add</button></div>';
2107
- return '<div class="set-row list"><div class="set-meta"><div class="set-name">' + labelWithBadges(s.label) + "</div>"
2126
+ return '<div class="set-row list"><div class="set-meta"><div class="set-name">' + settingIcon(s.key) + labelWithBadges(s.label) + "</div>"
2108
2127
  + '<div class="set-hint">' + esc(s.hint) + (s.globalOnly ? " · global" : "") + "</div>"
2109
2128
  + chips + adder + "</div></div>";
2110
2129
  }
@@ -2117,7 +2136,7 @@ export {
2117
2136
  + "</select>"
2118
2137
  : '<button type="button" class="toggle' + (s.value ? " on" : "") + '" data-skey="' + esc(s.key)
2119
2138
  + '" data-kind="bool" data-on="' + (s.value ? 1 : 0) + '">' + (s.value ? "On" : "Off") + "</button>";
2120
- return '<div class="set-row"><div class="set-meta"><div class="set-name">' + labelWithBadges(s.label) + "</div>"
2139
+ return '<div class="set-row"><div class="set-meta"><div class="set-name">' + settingIcon(s.key) + labelWithBadges(s.label) + "</div>"
2121
2140
  + '<div class="set-hint">' + esc(s.hint) + (s.globalOnly ? " · global" : "") + "</div></div>"
2122
2141
  + '<div class="set-ctl">' + ctl + "</div></div>";
2123
2142
  }
@@ -2331,7 +2350,7 @@ export {
2331
2350
 
2332
2351
  function onSavings() { const h = (location.hash || "").replace(/^#\/?/, ""); return h !== "settings" && h !== "skills"; }
2333
2352
  // One refresh of everything visible on the Savings view; `force` ignores auto-refresh-off.
2334
- function refreshAll(force) { poll(force); if (onSavings()) loadSystems(force); }
2353
+ function refreshAll(force) { return Promise.all([poll(force), onSavings() ? loadSystems(force) : null]); }
2335
2354
 
2336
2355
  // --- skills subpage (lists enigma + external skills over /api/skills) ---
2337
2356
  // App logos (Claude, OpenAI/Codex, OpenCode), recoloured to currentColor so a chip's
@@ -2616,7 +2635,14 @@ export {
2616
2635
  $("cfgImport").addEventListener("click", importConfig);
2617
2636
  $("updateBtn").addEventListener("click", runUpdate);
2618
2637
  $("fixPathBtn").addEventListener("click", runFixPath);
2619
- $("refreshBtn").addEventListener("click", () => refreshAll(true));
2638
+ $("refreshBtn").addEventListener("click", async () => {
2639
+ const b = $("refreshBtn");
2640
+ if (b.classList.contains("spinning")) return; // already refreshing
2641
+ b.classList.add("spinning");
2642
+ // Spin until the refresh settles, but at least one full turn so a fast refresh still reads.
2643
+ try { await Promise.all([refreshAll(true), new Promise((r) => setTimeout(r, 700))]); }
2644
+ finally { b.classList.remove("spinning"); }
2645
+ });
2620
2646
  window.addEventListener("hashchange", route);
2621
2647
  route();
2622
2648
  </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enigmax/dashboard",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
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": [