@enigmax/dashboard 0.1.53 → 0.1.55
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 +68 -3
- package/package.json +1 -1
package/assets/index.html
CHANGED
|
@@ -361,6 +361,13 @@
|
|
|
361
361
|
}
|
|
362
362
|
.modal-back.show .modal { transform: none; opacity: 1; }
|
|
363
363
|
.modal.wide { max-width: 900px; }
|
|
364
|
+
.as-top { display: flex; gap: 8px; margin: 14px 0 6px; }
|
|
365
|
+
.as-list { max-height: 50vh; overflow: auto; border: 1px solid var(--border); border-radius: 8px; padding: 4px 12px; }
|
|
366
|
+
.as-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--surface2); cursor: pointer; }
|
|
367
|
+
.as-row:last-child { border-bottom: none; }
|
|
368
|
+
.as-row input { accent-color: var(--accent); width: 15px; height: 15px; flex-shrink: 0; }
|
|
369
|
+
.as-name { font-size: 13px; color: var(--text); font-family: var(--mono); }
|
|
370
|
+
.as-src { margin-left: auto; display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; }
|
|
364
371
|
.modal-title { font-family: var(--mono); font-size: 14px; font-weight: 600; color: var(--text); }
|
|
365
372
|
.modal-msg { color: var(--muted); font-size: 13px; line-height: 1.5; margin-top: 8px; word-break: break-word; }
|
|
366
373
|
.modal-input {
|
|
@@ -2637,7 +2644,11 @@ export {
|
|
|
2637
2644
|
}
|
|
2638
2645
|
|
|
2639
2646
|
function memoryRow(g) {
|
|
2640
|
-
|
|
2647
|
+
// Apps are shown as brand logos (see AGENT_ICONS), never text - the label is the tooltip.
|
|
2648
|
+
const apps = '<span class="aicons">' + (g.agents || []).map((a) => {
|
|
2649
|
+
const ic = agentSvg(a.name);
|
|
2650
|
+
return ic ? '<span class="aicon" title="' + esc(a.label) + '" aria-label="' + esc(a.label) + '">' + ic + "</span>" : esc(a.label);
|
|
2651
|
+
}).join("") + "</span>";
|
|
2641
2652
|
const state = !g.deployed ? '<span class="tag">not deployed</span>'
|
|
2642
2653
|
: g.edited ? '<span class="tag exp">edited</span>'
|
|
2643
2654
|
: g.managed ? '<span class="tag meas">managed</span>' : '<span class="tag">custom</span>';
|
|
@@ -2950,7 +2961,7 @@ export {
|
|
|
2950
2961
|
$("skillsBody").innerHTML =
|
|
2951
2962
|
'<div class="panel"><div class="set-cat-h">Auto-detect skills by stack</div>'
|
|
2952
2963
|
+ '<div class="set-cat-b">Scan this project (packages, config files, gems) and install the matching community skills for its stack - hash-verified and kept separate from the policy skills below.</div>'
|
|
2953
|
-
+ '<div class="ladd"><button type="button" class="toggle on with-icon" data-proj-autoskills><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>Detect
|
|
2964
|
+
+ '<div class="ladd"><button type="button" class="toggle on with-icon" data-proj-autoskills><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>Detect skills</button></div></div>'
|
|
2954
2965
|
+ section("Policy skills", "Deploy enigma's policy skills into this project, per app. A green chip means the skill is in that app's project folder.", skills, "No skills.");
|
|
2955
2966
|
return;
|
|
2956
2967
|
}
|
|
@@ -3495,6 +3506,60 @@ export {
|
|
|
3495
3506
|
loadProjectsList(); // refresh selector badges
|
|
3496
3507
|
} catch { setNote(activeView === "skills" ? "skillsNote" : "settingsNote", "Could not reach the server.", "err"); }
|
|
3497
3508
|
}
|
|
3509
|
+
// Autoskills: detect the project's stack, let the user pick which community skills to
|
|
3510
|
+
// install (all checked by default), then install the selected ones - or cancel.
|
|
3511
|
+
async function autoskillsFlow() {
|
|
3512
|
+
if (!isProjectScope()) return;
|
|
3513
|
+
setNote("skillsNote", "Detecting your stack...");
|
|
3514
|
+
let out;
|
|
3515
|
+
try { out = await (await fetch("/api/projects/action", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ path: currentScope, op: "autoskills-detect" }) })).json(); }
|
|
3516
|
+
catch { setNote("skillsNote", "Could not reach the server.", "err"); return; }
|
|
3517
|
+
if (!out.ok) { setNote("skillsNote", out.error || "Detection failed.", "err"); return; }
|
|
3518
|
+
const skills = out.skills || [];
|
|
3519
|
+
if (!skills.length) { setNote("skillsNote", (out.detected ? "Detected " + out.detected + " - " : "") + "no matching community skills.", "ok"); return; }
|
|
3520
|
+
setNote("skillsNote", out.detected ? ("Detected " + out.detected + ".") : "");
|
|
3521
|
+
const selected = await openAutoskillsModal(out.detected, skills);
|
|
3522
|
+
if (selected == null) { setNote("skillsNote", "Cancelled."); return; }
|
|
3523
|
+
if (!selected.length) { setNote("skillsNote", "Nothing selected.", "err"); return; }
|
|
3524
|
+
setNote("skillsNote", "Installing " + selected.length + " skill(s)...");
|
|
3525
|
+
try {
|
|
3526
|
+
const ins = await (await fetch("/api/projects/action", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ path: currentScope, op: "autoskills-install", skills: selected }) })).json();
|
|
3527
|
+
if (ins.ok) { setNote("skillsNote", ins.note || "Installed.", "ok"); loadProjectSkills(); loadProjectsList(); }
|
|
3528
|
+
else setNote("skillsNote", ins.error || ins.note || "Install failed.", "err");
|
|
3529
|
+
} catch { setNote("skillsNote", "Could not reach the server.", "err"); }
|
|
3530
|
+
}
|
|
3531
|
+
|
|
3532
|
+
// Selection modal: one checkbox per detected skill (all checked), Select all / Clear, and
|
|
3533
|
+
// Install selected / Cancel. Resolves to the chosen refs, or null when cancelled.
|
|
3534
|
+
function openAutoskillsModal(detected, skills) {
|
|
3535
|
+
return new Promise((resolve) => {
|
|
3536
|
+
const back = document.createElement("div");
|
|
3537
|
+
back.className = "modal-back";
|
|
3538
|
+
const rows = skills.map((s, i) =>
|
|
3539
|
+
'<label class="as-row"><input type="checkbox" checked data-as="' + i + '">'
|
|
3540
|
+
+ '<span class="as-name">' + esc(s.name) + "</span>"
|
|
3541
|
+
+ '<span class="as-src">' + (s.sources || []).map((x) => '<span class="tag">' + esc(x) + "</span>").join("") + "</span></label>").join("");
|
|
3542
|
+
back.innerHTML = '<div class="modal wide" role="dialog" aria-modal="true">'
|
|
3543
|
+
+ '<div class="modal-title">Install stack skills</div>'
|
|
3544
|
+
+ '<div class="modal-msg">' + (detected ? "Detected " + esc(detected) + ". " : "") + esc(skills.length + " matching community skill(s)") + " - pick which to install. They are hash-verified and kept separate from the policy skills.</div>"
|
|
3545
|
+
+ '<div class="as-top"><button type="button" class="toggle" data-as-all>Select all</button><button type="button" class="toggle" data-as-none>Clear</button></div>'
|
|
3546
|
+
+ '<div class="as-list">' + rows + "</div>"
|
|
3547
|
+
+ '<div class="modal-acts"><button type="button" class="toggle modal-cancel">Cancel</button>'
|
|
3548
|
+
+ '<button type="button" class="toggle on modal-ok">Install selected</button></div></div>';
|
|
3549
|
+
document.body.appendChild(back);
|
|
3550
|
+
requestAnimationFrame(() => back.classList.add("show"));
|
|
3551
|
+
let done = false;
|
|
3552
|
+
const close = (val) => { if (done) return; done = true; back.classList.remove("show"); setTimeout(() => back.remove(), 200); resolve(val); };
|
|
3553
|
+
const boxes = () => Array.from(back.querySelectorAll("input[data-as]"));
|
|
3554
|
+
back.querySelector("[data-as-all]").addEventListener("click", () => boxes().forEach((b) => { b.checked = true; }));
|
|
3555
|
+
back.querySelector("[data-as-none]").addEventListener("click", () => boxes().forEach((b) => { b.checked = false; }));
|
|
3556
|
+
back.querySelector(".modal-cancel").addEventListener("click", () => close(null));
|
|
3557
|
+
back.addEventListener("mousedown", (e) => { if (e.target === back) close(null); });
|
|
3558
|
+
back.addEventListener("keydown", (e) => { if (e.key === "Escape") { e.preventDefault(); close(null); } });
|
|
3559
|
+
back.querySelector(".modal-ok").addEventListener("click", () => close(boxes().filter((b) => b.checked).map((b) => skills[Number(b.dataset.as)].ref)));
|
|
3560
|
+
});
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3498
3563
|
async function forgetProject() {
|
|
3499
3564
|
if (!isProjectScope()) return;
|
|
3500
3565
|
if (!(await askConfirm("Forget project", "Stop managing " + scopeLabel() + "? Its files are not touched.", false))) return;
|
|
@@ -3506,7 +3571,7 @@ export {
|
|
|
3506
3571
|
}
|
|
3507
3572
|
function onProjectControlClick(e) {
|
|
3508
3573
|
if (!isProjectScope()) return;
|
|
3509
|
-
if (e.target.closest("[data-proj-autoskills]")) {
|
|
3574
|
+
if (e.target.closest("[data-proj-autoskills]")) { autoskillsFlow(); return; }
|
|
3510
3575
|
const chip = e.target.closest("[data-pskill]"); // per-app deploy chip in the Skills view
|
|
3511
3576
|
if (chip) { projAction({ op: "skill", name: chip.dataset.pskill, agent: chip.dataset.pagent, on: chip.dataset.poff !== "1" }); return; }
|
|
3512
3577
|
const sk = e.target.closest("[data-proj-skill]");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enigmax/dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.55",
|
|
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": [
|