@enigmax/dashboard 0.1.53 → 0.1.54
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 +63 -2
- 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 {
|
|
@@ -2950,7 +2957,7 @@ export {
|
|
|
2950
2957
|
$("skillsBody").innerHTML =
|
|
2951
2958
|
'<div class="panel"><div class="set-cat-h">Auto-detect skills by stack</div>'
|
|
2952
2959
|
+ '<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
|
|
2960
|
+
+ '<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
2961
|
+ 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
2962
|
return;
|
|
2956
2963
|
}
|
|
@@ -3495,6 +3502,60 @@ export {
|
|
|
3495
3502
|
loadProjectsList(); // refresh selector badges
|
|
3496
3503
|
} catch { setNote(activeView === "skills" ? "skillsNote" : "settingsNote", "Could not reach the server.", "err"); }
|
|
3497
3504
|
}
|
|
3505
|
+
// Autoskills: detect the project's stack, let the user pick which community skills to
|
|
3506
|
+
// install (all checked by default), then install the selected ones - or cancel.
|
|
3507
|
+
async function autoskillsFlow() {
|
|
3508
|
+
if (!isProjectScope()) return;
|
|
3509
|
+
setNote("skillsNote", "Detecting your stack...");
|
|
3510
|
+
let out;
|
|
3511
|
+
try { out = await (await fetch("/api/projects/action", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ path: currentScope, op: "autoskills-detect" }) })).json(); }
|
|
3512
|
+
catch { setNote("skillsNote", "Could not reach the server.", "err"); return; }
|
|
3513
|
+
if (!out.ok) { setNote("skillsNote", out.error || "Detection failed.", "err"); return; }
|
|
3514
|
+
const skills = out.skills || [];
|
|
3515
|
+
if (!skills.length) { setNote("skillsNote", (out.detected ? "Detected " + out.detected + " - " : "") + "no matching community skills.", "ok"); return; }
|
|
3516
|
+
setNote("skillsNote", out.detected ? ("Detected " + out.detected + ".") : "");
|
|
3517
|
+
const selected = await openAutoskillsModal(out.detected, skills);
|
|
3518
|
+
if (selected == null) { setNote("skillsNote", "Cancelled."); return; }
|
|
3519
|
+
if (!selected.length) { setNote("skillsNote", "Nothing selected.", "err"); return; }
|
|
3520
|
+
setNote("skillsNote", "Installing " + selected.length + " skill(s)...");
|
|
3521
|
+
try {
|
|
3522
|
+
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();
|
|
3523
|
+
if (ins.ok) { setNote("skillsNote", ins.note || "Installed.", "ok"); loadProjectSkills(); loadProjectsList(); }
|
|
3524
|
+
else setNote("skillsNote", ins.error || ins.note || "Install failed.", "err");
|
|
3525
|
+
} catch { setNote("skillsNote", "Could not reach the server.", "err"); }
|
|
3526
|
+
}
|
|
3527
|
+
|
|
3528
|
+
// Selection modal: one checkbox per detected skill (all checked), Select all / Clear, and
|
|
3529
|
+
// Install selected / Cancel. Resolves to the chosen refs, or null when cancelled.
|
|
3530
|
+
function openAutoskillsModal(detected, skills) {
|
|
3531
|
+
return new Promise((resolve) => {
|
|
3532
|
+
const back = document.createElement("div");
|
|
3533
|
+
back.className = "modal-back";
|
|
3534
|
+
const rows = skills.map((s, i) =>
|
|
3535
|
+
'<label class="as-row"><input type="checkbox" checked data-as="' + i + '">'
|
|
3536
|
+
+ '<span class="as-name">' + esc(s.name) + "</span>"
|
|
3537
|
+
+ '<span class="as-src">' + (s.sources || []).map((x) => '<span class="tag">' + esc(x) + "</span>").join("") + "</span></label>").join("");
|
|
3538
|
+
back.innerHTML = '<div class="modal wide" role="dialog" aria-modal="true">'
|
|
3539
|
+
+ '<div class="modal-title">Install stack skills</div>'
|
|
3540
|
+
+ '<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>"
|
|
3541
|
+
+ '<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>'
|
|
3542
|
+
+ '<div class="as-list">' + rows + "</div>"
|
|
3543
|
+
+ '<div class="modal-acts"><button type="button" class="toggle modal-cancel">Cancel</button>'
|
|
3544
|
+
+ '<button type="button" class="toggle on modal-ok">Install selected</button></div></div>';
|
|
3545
|
+
document.body.appendChild(back);
|
|
3546
|
+
requestAnimationFrame(() => back.classList.add("show"));
|
|
3547
|
+
let done = false;
|
|
3548
|
+
const close = (val) => { if (done) return; done = true; back.classList.remove("show"); setTimeout(() => back.remove(), 200); resolve(val); };
|
|
3549
|
+
const boxes = () => Array.from(back.querySelectorAll("input[data-as]"));
|
|
3550
|
+
back.querySelector("[data-as-all]").addEventListener("click", () => boxes().forEach((b) => { b.checked = true; }));
|
|
3551
|
+
back.querySelector("[data-as-none]").addEventListener("click", () => boxes().forEach((b) => { b.checked = false; }));
|
|
3552
|
+
back.querySelector(".modal-cancel").addEventListener("click", () => close(null));
|
|
3553
|
+
back.addEventListener("mousedown", (e) => { if (e.target === back) close(null); });
|
|
3554
|
+
back.addEventListener("keydown", (e) => { if (e.key === "Escape") { e.preventDefault(); close(null); } });
|
|
3555
|
+
back.querySelector(".modal-ok").addEventListener("click", () => close(boxes().filter((b) => b.checked).map((b) => skills[Number(b.dataset.as)].ref)));
|
|
3556
|
+
});
|
|
3557
|
+
}
|
|
3558
|
+
|
|
3498
3559
|
async function forgetProject() {
|
|
3499
3560
|
if (!isProjectScope()) return;
|
|
3500
3561
|
if (!(await askConfirm("Forget project", "Stop managing " + scopeLabel() + "? Its files are not touched.", false))) return;
|
|
@@ -3506,7 +3567,7 @@ export {
|
|
|
3506
3567
|
}
|
|
3507
3568
|
function onProjectControlClick(e) {
|
|
3508
3569
|
if (!isProjectScope()) return;
|
|
3509
|
-
if (e.target.closest("[data-proj-autoskills]")) {
|
|
3570
|
+
if (e.target.closest("[data-proj-autoskills]")) { autoskillsFlow(); return; }
|
|
3510
3571
|
const chip = e.target.closest("[data-pskill]"); // per-app deploy chip in the Skills view
|
|
3511
3572
|
if (chip) { projAction({ op: "skill", name: chip.dataset.pskill, agent: chip.dataset.pagent, on: chip.dataset.poff !== "1" }); return; }
|
|
3512
3573
|
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.54",
|
|
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": [
|