@enigmax/dashboard 0.1.100 → 0.1.102

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 +98 -6
  2. package/package.json +1 -1
package/assets/index.html CHANGED
@@ -27,6 +27,10 @@
27
27
  --shadow-2: 0 18px 40px -22px rgba(0,0,0,0.75);
28
28
  }
29
29
  * { box-sizing: border-box; }
30
+ /* An icon never gives up its size to the text beside it. Every svg here is an icon, and
31
+ a flex item shrinks by default, so an account name or a toast message long enough to
32
+ fill the row squeezes the glyph down to a sliver. The text is what truncates. */
33
+ svg { flex-shrink: 0; }
30
34
  body {
31
35
  margin: 0 auto; color: var(--text); font-family: var(--sans);
32
36
  /* Atmospheric depth: a faint amber glow upper-right (the brand accent) and a
@@ -720,6 +724,10 @@
720
724
  .modal-ok.danger { background: var(--accent2); border-color: var(--accent2); color: #2a0f08; }
721
725
  .modal-ok:disabled { opacity: .45; cursor: not-allowed; }
722
726
  .modal-err { color: var(--bad); font-size: 12px; margin-top: 8px; }
727
+ /* The default value printed beside a setting hint: quiet, so it reads as an annotation
728
+ rather than a second label competing with the name. */
729
+ .set-def{opacity:.72;font-variant-numeric:tabular-nums;white-space:nowrap}
730
+ .set-ctl .gate-reset{flex:none}
723
731
  </style>
724
732
  </head>
725
733
  <body>
@@ -1118,7 +1126,7 @@
1118
1126
  <div class="set-grid">
1119
1127
  <div class="set-row">
1120
1128
  <div class="set-meta"><div class="set-name">Agent</div><div class="set-hint" id="gateAgentHint">which coding agent the pipeline drives</div></div>
1121
- <div class="set-ctl"><select id="gateAgent" class="linput"><option value="auto">auto</option><option value="claude">claude</option><option value="codex">codex</option><option value="opencode">opencode</option><option value="rovodev">rovodev</option><option value="pi">pi</option></select></div>
1129
+ <div class="set-ctl ladd" style="align-items:center"><select id="gateAgent" class="linput"><option value="auto">auto</option><option value="claude">claude</option><option value="codex">codex</option><option value="opencode">opencode</option><option value="rovodev">rovodev</option><option value="pi">pi</option></select></div>
1122
1130
  </div>
1123
1131
  <div class="set-row" id="gateModelRow">
1124
1132
  <div class="set-meta"><div class="set-name">Model</div><div class="set-hint" id="gateModelHint">the model every step runs on; a smaller one cuts run time and tokens</div></div>
@@ -1130,11 +1138,11 @@
1130
1138
  </div>
1131
1139
  <div class="set-row">
1132
1140
  <div class="set-meta"><div class="set-name">Log level</div><div class="set-hint">daemon output verbosity</div></div>
1133
- <div class="set-ctl"><select id="gateLogLevel" class="linput"><option value="debug">debug</option><option value="info">info</option><option value="warn">warn</option><option value="error">error</option></select></div>
1141
+ <div class="set-ctl ladd" style="align-items:center"><select id="gateLogLevel" class="linput"><option value="debug">debug</option><option value="info">info</option><option value="warn">warn</option><option value="error">error</option></select></div>
1134
1142
  </div>
1135
1143
  <div class="set-row">
1136
1144
  <div class="set-meta"><div class="set-name">Read intent from transcripts</div><div class="set-hint">feeds what you were trying to do to every step; off makes the agents work from the diff alone</div></div>
1137
- <div class="set-ctl"><button id="gateIntent" type="button" class="toggle" data-kind="bool" role="switch" aria-checked="false" aria-label="Read intent from transcripts"></button></div>
1145
+ <div class="set-ctl ladd" style="align-items:center"><button id="gateIntent" type="button" class="toggle" data-kind="bool" role="switch" aria-checked="false" aria-label="Read intent from transcripts"></button></div>
1138
1146
  </div>
1139
1147
  </div>
1140
1148
  <div class="set-cat-h" style="margin-top:18px">Auto-fix attempts</div>
@@ -4467,6 +4475,57 @@ export {
4467
4475
  btn.dataset.on = on ? "1" : "0";
4468
4476
  btn.setAttribute("aria-checked", on ? "true" : "false");
4469
4477
  }
4478
+ // Defaults for every gate setting, straight from the gate's own config loader (the
4479
+ // server resolves an empty config rather than keeping a second copy of the numbers).
4480
+ // They drive the "default N" hint and the per-row reset below.
4481
+ let gateDefaults = null;
4482
+ // A per-setting reset. These values are mostly small integers that get nudged once and
4483
+ // then forgotten, and nothing on screen said what they started as - so the default is
4484
+ // printed in the hint AND made one click away. Icon-only with a name, per the icon
4485
+ // action rules; the title carries the value so a disabled reset still answers "what
4486
+ // was it?" without the user opening the YAML.
4487
+ function resetBtn(key, def, label) {
4488
+ if (def === null || def === undefined) return "";
4489
+ return '<button type="button" class="iconbtn gate-reset" data-gate-reset="' + esc(key) + '" data-gate-def="' + esc(String(def)) + '" data-gate-label="' + esc(label) + '"'
4490
+ + ' title="Reset to the default (' + esc(String(def)) + ')" aria-label="Reset ' + esc(label) + ' to the default (' + esc(String(def)) + ')">'
4491
+ + '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 2v6h6"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L3 8"/></svg></button>';
4492
+ }
4493
+ /** Put (or refresh) a reset button in the cell of a control rendered by the static markup. */
4494
+ function mountGateReset(key, label, def) {
4495
+ const el = gateCtlFor(key);
4496
+ if (!el || def === null || def === undefined || def === "") return;
4497
+ const cell = el.parentElement;
4498
+ if (!cell) return;
4499
+ const old = cell.querySelector("[data-gate-reset]");
4500
+ if (old) old.remove();
4501
+ cell.insertAdjacentHTML("beforeend", resetBtn(key, def, label));
4502
+ }
4503
+ /** The control a reset key writes, so the button can compare against what is on screen. */
4504
+ function gateCtlFor(key) {
4505
+ if (key.indexOf("auto_fix.") === 0) return document.querySelector('[data-gate-af="' + key.slice("auto_fix.".length) + '"]');
4506
+ if (key === "agent") return $("gateAgent");
4507
+ if (key === "log_level") return $("gateLogLevel");
4508
+ if (key === "ci_timeout") return $("gateCiTimeout");
4509
+ if (key === "intent.enabled") return $("gateIntent");
4510
+ return null;
4511
+ }
4512
+ /** Current value of a reset-able control, as the string its default is compared against. */
4513
+ function gateCtlValue(key, el) {
4514
+ if (!el) return null;
4515
+ if (key === "intent.enabled") return el.dataset.on === "1" ? "true" : "false";
4516
+ return String(el.value).trim();
4517
+ }
4518
+ // Nothing to reset is not the same as nothing to say: the button stays visible and
4519
+ // disabled (never hidden), because its title is where the default value is written.
4520
+ function syncGateResets() {
4521
+ for (const b of document.querySelectorAll("[data-gate-reset]")) {
4522
+ const key = b.dataset.gateReset;
4523
+ const cur = gateCtlValue(key, gateCtlFor(key));
4524
+ const isDefault = cur !== null && cur === b.dataset.gateDef;
4525
+ b.disabled = isDefault || !gateWritable;
4526
+ b.title = isDefault ? "Already the default (" + b.dataset.gateDef + ")" : "Reset to the default (" + b.dataset.gateDef + ")";
4527
+ }
4528
+ }
4470
4529
  const GATE_AUTOFIX = [
4471
4530
  ["rebase", "conflicts and stale-base failures"],
4472
4531
  ["review", "0 by default: review findings are yours to approve"],
@@ -4556,12 +4615,26 @@ export {
4556
4615
  $("gateLogLevel").value = s.logLevel;
4557
4616
  syncDD($("gateLogLevel"));
4558
4617
  gateBool($("gateIntent"), s.intentEnabled);
4559
- $("gateAutoFix").innerHTML = GATE_AUTOFIX.map(([step, hint]) =>
4560
- '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(step) + '</div><div class="set-hint">' + esc(hint) + "</div></div>"
4561
- + '<div class="set-ctl"><input type="number" min="0" max="20" step="1" inputmode="numeric" class="linput" style="flex:none;width:80px" data-gate-af="' + esc(step) + '" value="' + Number(s.autoFix[step] || 0) + '"></div></div>').join("");
4618
+ gateDefaults = s.defaults || null;
4619
+ $("gateAutoFix").innerHTML = GATE_AUTOFIX.map(([step, hint]) => {
4620
+ const def = gateDefaults && gateDefaults.autoFix ? Number(gateDefaults.autoFix[step] || 0) : null;
4621
+ return '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(step) + '</div><div class="set-hint">' + esc(hint)
4622
+ + (def === null ? "" : ' <span class="set-def">default ' + def + "</span>") + "</div></div>"
4623
+ + '<div class="set-ctl ladd" style="align-items:center"><input type="number" min="0" max="20" step="1" inputmode="numeric" class="linput" style="flex:none;width:80px" data-gate-af="' + esc(step) + '" value="' + Number(s.autoFix[step] || 0) + '">'
4624
+ + resetBtn("auto_fix." + step, def, "the " + step + " limit") + "</div></div>";
4625
+ }).join("");
4626
+ // The fixed rows get their reset mounted here rather than in the static markup,
4627
+ // because only the payload knows what each default actually is.
4628
+ if (gateDefaults) {
4629
+ mountGateReset("agent", "the agent", gateDefaults.agent);
4630
+ mountGateReset("log_level", "the log level", gateDefaults.logLevel);
4631
+ mountGateReset("ci_timeout", "the CI timeout", gateDefaults.ciTimeout);
4632
+ mountGateReset("intent.enabled", "intent extraction", String(gateDefaults.intentEnabled));
4633
+ }
4562
4634
  const ctls = panel.querySelectorAll("input, select, button");
4563
4635
  for (const c of ctls) c.disabled = !gateWritable;
4564
4636
  syncGateModelSave(); // after the sweep, which would otherwise re-enable a clean Save
4637
+ syncGateResets(); // likewise: the sweep would re-enable a reset with nothing to reset
4565
4638
  }
4566
4639
  // `live` marks a poll refresh: it repaints the run strip and the two switches, and leaves
4567
4640
  // every editor alone so a poll can never overwrite what is being typed.
@@ -4719,6 +4792,25 @@ export {
4719
4792
  const step = e.target.dataset && e.target.dataset.gateAf;
4720
4793
  if (step) saveGateSetting("auto_fix." + step, Number(e.target.value), "the " + step + " limit");
4721
4794
  });
4795
+ // Live feedback while typing: the reset greys out the moment the value is back to the
4796
+ // default, so the row answers "am I still overriding this?" without a save.
4797
+ $("gateAutoFix").addEventListener("input", syncGateResets);
4798
+ $("gateCiTimeout").addEventListener("input", syncGateResets);
4799
+ // One delegated handler for every reset in the panel. The write goes through the same
4800
+ // saveGateSetting as the control itself, so it reloads the gate afterwards - which is
4801
+ // what reconciles the optimistic paint and rolls it back if the save did not land.
4802
+ $("gateSetPanel").addEventListener("click", (e) => {
4803
+ const b = e.target.closest && e.target.closest("[data-gate-reset]");
4804
+ if (!b || b.disabled) return;
4805
+ const key = b.dataset.gateReset, def = b.dataset.gateDef, el = gateCtlFor(key);
4806
+ if (el) {
4807
+ if (key === "intent.enabled") gateBool(el, def === "true");
4808
+ else { el.value = def; if (el.tagName === "SELECT") syncDD(el); }
4809
+ syncGateResets();
4810
+ }
4811
+ const value = key.indexOf("auto_fix.") === 0 ? Number(def) : (key === "intent.enabled" ? def === "true" : def);
4812
+ saveGateSetting(key, value, b.dataset.gateLabel);
4813
+ });
4722
4814
 
4723
4815
  // --- section rail (a full-screen drawer below 900px) ---
4724
4816
  const sidebarEl = $("sidebar"), navScrimEl = $("navScrim"), navOpenBtn = $("navOpen");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enigmax/dashboard",
3
- "version": "0.1.100",
3
+ "version": "0.1.102",
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": [