@enigmax/dashboard 0.1.69 → 0.1.71
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 +107 -1
- package/package.json +1 -1
package/assets/index.html
CHANGED
|
@@ -526,6 +526,7 @@
|
|
|
526
526
|
<a href="#/settings" data-view="settings" class="tab">Settings</a>
|
|
527
527
|
<a href="#/resources" data-view="resources" class="tab">Resources</a>
|
|
528
528
|
<a href="#/recall" data-view="recall" class="tab">Recall</a>
|
|
529
|
+
<a href="#/codegraph" data-view="codegraph" class="tab">Code graph</a>
|
|
529
530
|
<a href="#/packs" data-view="packs" class="tab">Packs</a>
|
|
530
531
|
</nav>
|
|
531
532
|
<div class="nav">
|
|
@@ -873,6 +874,24 @@
|
|
|
873
874
|
<div id="recallList"></div>
|
|
874
875
|
</section>
|
|
875
876
|
|
|
877
|
+
<section id="view-codegraph" style="display:none">
|
|
878
|
+
<div class="page-head" style="display:flex;align-items:flex-start;gap:16px">
|
|
879
|
+
<div style="flex:1">
|
|
880
|
+
<h1>Code graph</h1>
|
|
881
|
+
<p>Structural memory of your codebase - a native knowledge graph of the symbols each file defines, the modules it imports and cross-file references. Enabling it exposes enigma's code-graph tools to your agents over MCP. Complements <a href="#/recall">Recall</a> (session memory).</p>
|
|
882
|
+
</div>
|
|
883
|
+
<button id="cgToggle" type="button" class="toggle" data-kind="bool" role="switch" aria-checked="false" aria-label="Code graph">Off</button>
|
|
884
|
+
<button id="cgRefresh" type="button" class="iconbtn" title="Refresh" aria-label="Refresh"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M23 4v6h-6"/><path d="M1 20v-6h6"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg></button>
|
|
885
|
+
</div>
|
|
886
|
+
<div id="cgNote" class="set-note"></div>
|
|
887
|
+
<div class="panel">
|
|
888
|
+
<div class="set-cat-h">Index a project</div>
|
|
889
|
+
<div class="set-hint" style="padding:4px 0">Build the graph for a codebase. Leave the path empty to index enigma's own working directory.</div>
|
|
890
|
+
<div class="ladd" style="gap:8px;flex-wrap:wrap"><input id="cgRoot" class="linput" placeholder="absolute project path (optional)" style="flex:1;min-width:240px"><button id="cgIndex" type="button" class="toggle on">Index</button></div>
|
|
891
|
+
</div>
|
|
892
|
+
<div id="cgBody"><div class="empty" style="padding:12px 0">Loading...</div></div>
|
|
893
|
+
</section>
|
|
894
|
+
|
|
876
895
|
<footer>
|
|
877
896
|
Polling pauses automatically when this tab is hidden, and the server caches each snapshot - so an open dashboard costs almost nothing.
|
|
878
897
|
Data source: <code>~/.enigma/ccr/stats.json</code> + <code>history.jsonl</code> - reset it with <code>enigma compress --clear</code>. Enigma <span id="ver"></span>
|
|
@@ -3156,6 +3175,7 @@ export {
|
|
|
3156
3175
|
const guard = (sec.guardProtects || []).map((p) => '<span class="tag">' + esc(p) + "</span>").join("");
|
|
3157
3176
|
el.innerHTML =
|
|
3158
3177
|
item("Context compression (MCP) · measured", boolPill(s.compress))
|
|
3178
|
+
+ item("Codebase memory / code graph (MCP)", boolPill(s.codeGraph))
|
|
3159
3179
|
+ item("Real tool-usage stats · measured", boolPill(s.usageStats))
|
|
3160
3180
|
+ item("Claude Code proxy · experimental", boolPill(s.proxy))
|
|
3161
3181
|
+ item("Live usage windows · real", boolPill(s.usageApi))
|
|
@@ -3713,7 +3733,7 @@ export {
|
|
|
3713
3733
|
|
|
3714
3734
|
// --- hash routing between the subpages ---
|
|
3715
3735
|
// Single source of truth for the subpage ids (savings is the default/fallback).
|
|
3716
|
-
const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources", "recall", "packs"];
|
|
3736
|
+
const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources", "recall", "codegraph", "packs"];
|
|
3717
3737
|
// Views that apply to a single project; the rest are global/machine-level only.
|
|
3718
3738
|
const PROJECT_VIEWS = ["settings", "skills"];
|
|
3719
3739
|
let activeView = "savings";
|
|
@@ -3734,6 +3754,7 @@ export {
|
|
|
3734
3754
|
else if (v === "usage") { loadProviderStatus(); if (!usageAccountsLoaded) { usageAccountsLoaded = true; loadUsageAccounts(); } }
|
|
3735
3755
|
else if (v === "resources") loadResources();
|
|
3736
3756
|
else if (v === "recall") loadRecall();
|
|
3757
|
+
else if (v === "codegraph") loadCodeGraph();
|
|
3737
3758
|
else if (v === "packs") loadPacks();
|
|
3738
3759
|
// The chart was sized while its view may have been hidden; nudge it on return.
|
|
3739
3760
|
else if (v === "savings") { loadSystems(true); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
|
|
@@ -3857,6 +3878,90 @@ export {
|
|
|
3857
3878
|
});
|
|
3858
3879
|
}
|
|
3859
3880
|
|
|
3881
|
+
// --- code graph subpage (native structural code memory) ---
|
|
3882
|
+
let cgProject = null;
|
|
3883
|
+
// Render one architecture/schema field defensively (arrays -> chips, objects -> count).
|
|
3884
|
+
function cgItem(x) {
|
|
3885
|
+
if (x && typeof x === "object") return String(x.name || x.label || x.path || x.id || JSON.stringify(x)).slice(0, 60);
|
|
3886
|
+
return String(x);
|
|
3887
|
+
}
|
|
3888
|
+
function cgVal(v) {
|
|
3889
|
+
if (Array.isArray(v)) {
|
|
3890
|
+
if (!v.length) return '<span class="tag">none</span>';
|
|
3891
|
+
return v.slice(0, 16).map((x) => '<span class="tag">' + esc(cgItem(x)) + "</span>").join(" ")
|
|
3892
|
+
+ (v.length > 16 ? ' <span class="set-hint">+' + (v.length - 16) + " more</span>" : "");
|
|
3893
|
+
}
|
|
3894
|
+
if (v && typeof v === "object") {
|
|
3895
|
+
const ents = Object.entries(v);
|
|
3896
|
+
if (!ents.length) return '<span class="tag">none</span>';
|
|
3897
|
+
return ents.slice(0, 16).map(([k, n]) => '<span class="tag">' + esc(k) + " " + esc(String(n)) + "</span>").join(" ");
|
|
3898
|
+
}
|
|
3899
|
+
return esc(String(v));
|
|
3900
|
+
}
|
|
3901
|
+
function cgSection(title, obj) {
|
|
3902
|
+
const entries = obj && typeof obj === "object" ? Object.entries(obj) : [];
|
|
3903
|
+
if (!entries.length) return "";
|
|
3904
|
+
return '<div class="panel"><div class="set-cat-h">' + esc(title) + "</div>"
|
|
3905
|
+
+ entries.map(([k, v]) => '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(k) + '</div><div class="set-hint">' + cgVal(v) + "</div></div></div>").join("")
|
|
3906
|
+
+ "</div>";
|
|
3907
|
+
}
|
|
3908
|
+
function renderCodeGraph(view) {
|
|
3909
|
+
view = view || {};
|
|
3910
|
+
const on = !!view.enabled;
|
|
3911
|
+
const t = $("cgToggle");
|
|
3912
|
+
t.classList.toggle("on", on); t.setAttribute("aria-checked", on ? "true" : "false"); t.textContent = on ? "On" : "Off";
|
|
3913
|
+
const projects = view.projects || [];
|
|
3914
|
+
const projHtml = projects.length
|
|
3915
|
+
? projects.map((p) => {
|
|
3916
|
+
const sel = p.name === view.selected;
|
|
3917
|
+
return '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(p.name)
|
|
3918
|
+
+ (sel ? ' <span class="tag" style="color:var(--ok)">viewing</span>' : "")
|
|
3919
|
+
+ '</div><div class="set-hint">' + esc((p.files || 0) + " files · " + (p.symbols || 0) + " symbols · " + (p.root || "")) + '</div></div>'
|
|
3920
|
+
+ '<div class="set-ctl"><button type="button" class="toggle' + (sel ? " on" : "") + '" data-cg-project="' + esc(p.name) + '">View</button></div></div>';
|
|
3921
|
+
}).join("")
|
|
3922
|
+
: '<div class="empty" style="padding:12px 0">No projects indexed yet. Use "Index a project" above.</div>';
|
|
3923
|
+
let html = '<div class="panel"><div class="set-cat-h">Indexed projects <small>' + projects.length + "</small></div>" + projHtml + "</div>";
|
|
3924
|
+
if (view.architecture) html += cgSection("Architecture" + (view.selected ? " - " + view.selected : ""), view.architecture);
|
|
3925
|
+
if (view.schema) html += cgSection("Graph schema", view.schema);
|
|
3926
|
+
$("cgBody").innerHTML = html;
|
|
3927
|
+
}
|
|
3928
|
+
async function loadCodeGraph() {
|
|
3929
|
+
$("cgNote").textContent = "";
|
|
3930
|
+
try {
|
|
3931
|
+
const q = cgProject ? "?project=" + encodeURIComponent(cgProject) : "";
|
|
3932
|
+
renderCodeGraph(await (await fetch("/api/codegraph" + q, { cache: "no-store" })).json());
|
|
3933
|
+
} catch { $("cgBody").innerHTML = '<div class="empty" style="padding:12px 0">Code graph unavailable.</div>'; }
|
|
3934
|
+
}
|
|
3935
|
+
async function cgPost(payload, working) {
|
|
3936
|
+
setNote("cgNote", working);
|
|
3937
|
+
try {
|
|
3938
|
+
const out = await (await fetch("/api/codegraph", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) })).json();
|
|
3939
|
+
if (out.view) renderCodeGraph(out.view);
|
|
3940
|
+
return out;
|
|
3941
|
+
} catch { setNote("cgNote", "Could not reach the server.", "err"); return null; }
|
|
3942
|
+
}
|
|
3943
|
+
async function cgToggleAction() {
|
|
3944
|
+
const turnOn = $("cgToggle").getAttribute("aria-checked") !== "true";
|
|
3945
|
+
const out = await cgPost({ op: "toggle", on: turnOn, project: cgProject || undefined }, "Saving...");
|
|
3946
|
+
if (out) setNote("cgNote", out.ok ? (turnOn ? "Code graph enabled - tools exposed to your agents (restart them to load it)." : "Code graph disabled.") : (out.error || "Action failed."), out.ok ? "ok" : "err");
|
|
3947
|
+
}
|
|
3948
|
+
async function cgIndexAction() {
|
|
3949
|
+
const root = $("cgRoot").value.trim();
|
|
3950
|
+
const out = await cgPost({ op: "index", root: root || undefined }, "Indexing...");
|
|
3951
|
+
if (out) { if (out.view && out.view.selected) cgProject = out.view.selected; setNote("cgNote", out.ok ? "Indexed." : (out.error || "Indexing failed."), out.ok ? "ok" : "err"); }
|
|
3952
|
+
}
|
|
3953
|
+
function wireCodeGraph() {
|
|
3954
|
+
$("cgRefresh").addEventListener("click", loadCodeGraph);
|
|
3955
|
+
$("cgToggle").addEventListener("click", cgToggleAction);
|
|
3956
|
+
$("cgIndex").addEventListener("click", cgIndexAction);
|
|
3957
|
+
$("view-codegraph").addEventListener("click", (e) => {
|
|
3958
|
+
const b = e.target.closest("button[data-cg-project]");
|
|
3959
|
+
if (!b) return;
|
|
3960
|
+
cgProject = b.dataset.cgProject;
|
|
3961
|
+
loadCodeGraph();
|
|
3962
|
+
});
|
|
3963
|
+
}
|
|
3964
|
+
|
|
3860
3965
|
// --- recall subpage (local session memory; reads your own transcripts) ---
|
|
3861
3966
|
const RECALL_TYPES = ["bugfix", "feature", "refactor", "change", "discovery", "decision", "security"];
|
|
3862
3967
|
let recallProjSig = "";
|
|
@@ -4425,6 +4530,7 @@ export {
|
|
|
4425
4530
|
wireUsage();
|
|
4426
4531
|
wireResources();
|
|
4427
4532
|
wireRecall();
|
|
4533
|
+
wireCodeGraph();
|
|
4428
4534
|
wirePacks();
|
|
4429
4535
|
wireScope();
|
|
4430
4536
|
enhanceAll(); // upgrade every static <select> into the shared pretty dropdown
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enigmax/dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.71",
|
|
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": [
|