@enigmax/dashboard 0.1.50 → 0.1.52
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 +101 -0
- package/package.json +1 -1
package/assets/index.html
CHANGED
|
@@ -156,6 +156,9 @@
|
|
|
156
156
|
.set-meta { flex: 1; min-width: 0; }
|
|
157
157
|
.set-name { font-size: 13px; color: var(--text); }
|
|
158
158
|
.set-hint { font-size: 12px; color: var(--muted); margin-top: 2px; }
|
|
159
|
+
/* Capitalize the first letter of every option description/blurb in the dashboard
|
|
160
|
+
(presentation only; the shared registry keeps its lowercase TUI/CLI convention). */
|
|
161
|
+
.set-hint::first-letter, .set-cat-b::first-letter, .lnone::first-letter { text-transform: uppercase; }
|
|
159
162
|
.set-ctl { flex-shrink: 0; }
|
|
160
163
|
.toggle {
|
|
161
164
|
background: var(--surface2); color: var(--muted); border: 1px solid var(--border);
|
|
@@ -357,6 +360,7 @@
|
|
|
357
360
|
transform: translateY(10px) scale(.98); opacity: 0; transition: transform .18s ease, opacity .18s ease;
|
|
358
361
|
}
|
|
359
362
|
.modal-back.show .modal { transform: none; opacity: 1; }
|
|
363
|
+
.modal.wide { max-width: 900px; }
|
|
360
364
|
.modal-title { font-family: var(--mono); font-size: 14px; font-weight: 600; color: var(--text); }
|
|
361
365
|
.modal-msg { color: var(--muted); font-size: 13px; line-height: 1.5; margin-top: 8px; word-break: break-word; }
|
|
362
366
|
.modal-input {
|
|
@@ -660,6 +664,11 @@
|
|
|
660
664
|
</div>
|
|
661
665
|
</div>
|
|
662
666
|
</div>
|
|
667
|
+
<div class="panel" id="memPanel">
|
|
668
|
+
<div class="set-cat-h">Agent memory</div>
|
|
669
|
+
<div class="set-cat-b">Customize the instruction files enigma deploys to each agent (CLAUDE.md / AGENTS.md). Your edits follow the Customized-skill updates policy on the next sync: overwrite restores the managed version, keep preserves them.</div>
|
|
670
|
+
<div id="memBody" class="set-grid"><div class="empty" style="padding:12px 0">Loading...</div></div>
|
|
671
|
+
</div>
|
|
663
672
|
</section>
|
|
664
673
|
|
|
665
674
|
<section id="view-resources" style="display:none">
|
|
@@ -2587,10 +2596,12 @@ export {
|
|
|
2587
2596
|
async function loadSettings() {
|
|
2588
2597
|
if (isProjectScope()) return loadProjectSettings();
|
|
2589
2598
|
const port = $("dashPortPanel"); if (port) port.style.display = ""; // global-only panel
|
|
2599
|
+
const mp = $("memPanel"); if (mp) mp.style.display = "";
|
|
2590
2600
|
try {
|
|
2591
2601
|
const data = await (await fetch("/api/settings", { cache: "no-store" })).json();
|
|
2592
2602
|
renderSettings(data.categories || []);
|
|
2593
2603
|
renderDashPort(data);
|
|
2604
|
+
loadMemory();
|
|
2594
2605
|
} catch { $("settingsBody").innerHTML = '<div class="empty" style="padding:24px 0">Settings unavailable.</div>'; }
|
|
2595
2606
|
}
|
|
2596
2607
|
|
|
@@ -2614,6 +2625,93 @@ export {
|
|
|
2614
2625
|
} catch { setNote("settingsNote", "Could not reach the server to set the port.", "err"); }
|
|
2615
2626
|
}
|
|
2616
2627
|
|
|
2628
|
+
// --- agent memory editor (CLAUDE.md / AGENTS.md), global or per project ----------
|
|
2629
|
+
// Global edits each agent's home memory file; project scope edits <project>/CLAUDE.md
|
|
2630
|
+
// and <project>/AGENTS.md. Writes go through /api/memory, which marks the edit so the
|
|
2631
|
+
// overwrite/keep policy (Customized-skill updates) can preserve it on the next sync.
|
|
2632
|
+
function memProject() { return isProjectScope() ? currentScope : undefined; }
|
|
2633
|
+
|
|
2634
|
+
async function postMemory(id, action, content, project) {
|
|
2635
|
+
const body = { id, action };
|
|
2636
|
+
if (content !== undefined) body.content = content;
|
|
2637
|
+
if (project) body.path = project;
|
|
2638
|
+
return (await fetch("/api/memory", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) })).json();
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
function memoryRow(g) {
|
|
2642
|
+
const apps = (g.agents || []).map((a) => '<span class="tag">' + esc(a.label) + "</span>").join(" ");
|
|
2643
|
+
const state = !g.deployed ? '<span class="tag">not deployed</span>'
|
|
2644
|
+
: g.edited ? '<span class="tag exp">edited</span>'
|
|
2645
|
+
: g.managed ? '<span class="tag meas">managed</span>' : '<span class="tag">custom</span>';
|
|
2646
|
+
const reset = (g.deployed && g.edited) ? ' <button type="button" class="iconbtn" data-mem-reset="' + esc(g.id) + '" title="Restore the managed version">reset</button>' : "";
|
|
2647
|
+
return '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(g.file) + " " + apps + " " + state + "</div>"
|
|
2648
|
+
+ '<div class="set-hint">' + (g.deployed ? "Deployed - edit to customize" : "Not deployed yet - editing creates it") + "</div></div>"
|
|
2649
|
+
+ '<div class="set-ctl ladd" style="align-items:center"><button type="button" class="toggle on" data-mem-edit="' + esc(g.id) + '">Edit</button>' + reset + "</div></div>";
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
async function loadMemory(project) {
|
|
2653
|
+
const el = project ? $("projMemBody") : $("memBody");
|
|
2654
|
+
if (!el) return;
|
|
2655
|
+
try {
|
|
2656
|
+
const q = project ? "/api/memory?path=" + encodeURIComponent(project) : "/api/memory";
|
|
2657
|
+
const data = await (await fetch(q, { cache: "no-store" })).json();
|
|
2658
|
+
const groups = data.groups || [];
|
|
2659
|
+
el.innerHTML = groups.length ? groups.map(memoryRow).join("")
|
|
2660
|
+
: '<div class="empty" style="padding:12px 0">No agents detected to deploy memory to.</div>';
|
|
2661
|
+
} catch { el.innerHTML = '<div class="empty" style="padding:12px 0">Memory unavailable.</div>'; }
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
async function openMemoryEditor(id, project) {
|
|
2665
|
+
let out;
|
|
2666
|
+
try { out = await postMemory(id, "read", undefined, project); }
|
|
2667
|
+
catch { toast("Could not reach the server.", "err"); return; }
|
|
2668
|
+
if (!out.ok) { toast(out.error || "Could not read the memory file.", "err"); return; }
|
|
2669
|
+
const back = document.createElement("div");
|
|
2670
|
+
back.className = "modal-back";
|
|
2671
|
+
back.innerHTML = '<div class="modal wide" role="dialog" aria-modal="true">'
|
|
2672
|
+
+ '<div class="modal-title">Editing ' + esc(id) + " memory</div>"
|
|
2673
|
+
+ '<div class="modal-msg">Saved to every agent that reads this file. On the next sync the Customized-skill updates policy decides whether your edit is kept or replaced by the managed version.</div>'
|
|
2674
|
+
+ '<textarea class="editor" spellcheck="false" style="margin-top:14px"></textarea>'
|
|
2675
|
+
+ '<div class="modal-acts"><button type="button" class="toggle modal-cancel">Cancel</button>'
|
|
2676
|
+
+ '<button type="button" class="toggle on modal-ok">Save</button></div></div>';
|
|
2677
|
+
document.body.appendChild(back);
|
|
2678
|
+
requestAnimationFrame(() => back.classList.add("show"));
|
|
2679
|
+
const ta = back.querySelector("textarea");
|
|
2680
|
+
ta.value = out.content || "";
|
|
2681
|
+
let done = false;
|
|
2682
|
+
const close = () => { if (done) return; done = true; back.classList.remove("show"); setTimeout(() => back.remove(), 200); };
|
|
2683
|
+
back.querySelector(".modal-cancel").addEventListener("click", close);
|
|
2684
|
+
back.addEventListener("mousedown", (e) => { if (e.target === back) close(); });
|
|
2685
|
+
back.addEventListener("keydown", (e) => { if (e.key === "Escape") { e.preventDefault(); close(); } });
|
|
2686
|
+
back.querySelector(".modal-ok").addEventListener("click", async () => {
|
|
2687
|
+
const btn = back.querySelector(".modal-ok"); btn.disabled = true;
|
|
2688
|
+
try {
|
|
2689
|
+
const r = await postMemory(id, "save", ta.value, project);
|
|
2690
|
+
if (r.ok) { toast(r.note || "Saved.", "ok"); close(); loadMemory(project); }
|
|
2691
|
+
else { toast(r.error || "Could not save.", "err"); btn.disabled = false; }
|
|
2692
|
+
} catch { toast("Could not reach the server.", "err"); btn.disabled = false; }
|
|
2693
|
+
});
|
|
2694
|
+
ta.focus();
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
async function resetMemory(id, project) {
|
|
2698
|
+
if (!(await askConfirm("Restore managed memory?", "This replaces your edits with enigma's managed version.", true))) return;
|
|
2699
|
+
try {
|
|
2700
|
+
const r = await postMemory(id, "reset", undefined, project);
|
|
2701
|
+
if (r.ok) { toast(r.note || "Restored.", "ok"); loadMemory(project); }
|
|
2702
|
+
else toast(r.error || "Could not restore.", "err");
|
|
2703
|
+
} catch { toast("Could not reach the server.", "err"); }
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
// Edit/reset buttons live in both the global panel (#memBody) and the project view
|
|
2707
|
+
// (#projMemBody, mutually exclusive by scope); one document-level delegate covers both.
|
|
2708
|
+
document.addEventListener("click", (e) => {
|
|
2709
|
+
const ed = e.target.closest && e.target.closest("[data-mem-edit]");
|
|
2710
|
+
if (ed) { openMemoryEditor(ed.dataset.memEdit, memProject()); return; }
|
|
2711
|
+
const rs = e.target.closest && e.target.closest("[data-mem-reset]");
|
|
2712
|
+
if (rs) { resetMemory(rs.dataset.memReset, memProject()); }
|
|
2713
|
+
});
|
|
2714
|
+
|
|
2617
2715
|
// --- "Enigma systems" overview (factual state, never invented savings) ---
|
|
2618
2716
|
function pill(state, text) { return '<span class="pill ' + state + '">' + esc(text) + "</span>"; }
|
|
2619
2717
|
function boolPill(b) { return b ? pill("on", "on") : pill("off", "off"); }
|
|
@@ -3336,6 +3434,7 @@ export {
|
|
|
3336
3434
|
async function loadProjectSettings() {
|
|
3337
3435
|
const el = $("settingsBody");
|
|
3338
3436
|
const port = $("dashPortPanel"); if (port) port.style.display = "none"; // global-only
|
|
3437
|
+
const mp = $("memPanel"); if (mp) mp.style.display = "none"; // global panel; project memory is rendered inline below
|
|
3339
3438
|
el.innerHTML = '<div class="empty" style="padding:24px 0">Loading...</div>';
|
|
3340
3439
|
let d; try { d = await fetchProjectDetail(); } catch { el.innerHTML = '<div class="empty" style="padding:24px 0">Project unavailable.</div>'; return; }
|
|
3341
3440
|
if (d.error) { el.innerHTML = '<div class="empty" style="padding:24px 0">' + esc(d.error) + "</div>"; return; }
|
|
@@ -3350,11 +3449,13 @@ export {
|
|
|
3350
3449
|
+ '<div class="set-row"><div class="set-meta"><div class="set-name">Git hooks</div><div class="set-hint">commit secret guard for this repo</div></div><div class="set-ctl">' + hooks + "</div></div>"
|
|
3351
3450
|
+ '<div class="set-row"><div class="set-meta"><div class="set-name">AI quality gate</div><div class="set-hint">validate changes through the gate pipeline before push (needs the gate feature enabled)</div></div><div class="set-ctl">' + gate + "</div></div></div></div>"
|
|
3352
3451
|
+ '<div class="panel"><div class="set-cat-h">Project config</div><div class="set-cat-b">applies only to this project (its .enigma.json). Settings not shown here are global-only - switch Scope to Global for those.</div><div class="set-grid">' + cfg + "</div></div>"
|
|
3452
|
+
+ '<div class="panel"><div class="set-cat-h">Agent memory</div><div class="set-cat-b">Customize this project\'s instruction files (CLAUDE.md / AGENTS.md). Edits follow the Customized-skill updates policy on the next sync.</div><div id="projMemBody" class="set-grid"><div class="empty" style="padding:12px 0">Loading...</div></div></div>'
|
|
3353
3453
|
+ '<div class="panel"><div class="set-cat-h">Manage</div><div class="set-grid">'
|
|
3354
3454
|
+ '<div class="set-row"><div class="set-meta"><div class="set-name">Name</div><div class="set-hint">display name for this project (must be unique)</div></div><div class="set-ctl"><input class="linput" data-proj-name value="' + esc(d.label) + '" spellcheck="false" style="width:200px"></div></div>'
|
|
3355
3455
|
+ '<div class="set-row"><div class="set-meta"><div class="set-name">Description</div><div class="set-hint">optional note shown in the header</div></div><div class="set-ctl ladd" style="align-items:center"><input class="linput" data-proj-desc value="' + esc(d.description || "") + '" placeholder="optional" style="flex:none;width:240px"><button type="button" class="toggle on" data-proj-save-meta>Save</button></div></div>'
|
|
3356
3456
|
+ '<div class="set-row"><div class="set-meta"><div class="set-name">Forget project</div><div class="set-hint">remove it from the dashboard list - its files are not touched</div></div><div class="set-ctl"><button type="button" class="toggle" data-proj-forget>Forget</button></div></div>'
|
|
3357
3457
|
+ "</div></div>";
|
|
3458
|
+
loadMemory(currentScope);
|
|
3358
3459
|
}
|
|
3359
3460
|
async function saveProjectMeta() {
|
|
3360
3461
|
if (!isProjectScope()) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enigmax/dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.52",
|
|
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": [
|