@enigmax/dashboard 0.1.47 → 0.1.48

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 +30 -11
  2. package/package.json +1 -1
package/assets/index.html CHANGED
@@ -2781,8 +2781,19 @@ export {
2781
2781
  // Brand icon + text in one inline tag; the icon is omitted when there is none (e.g. content types).
2782
2782
  function iconLabel(ic, text) { return '<span class="srctag">' + (ic ? '<span class="aicon">' + ic + '</span>' : '') + esc(text) + '</span>'; }
2783
2783
 
2784
+ let skillScope = "global"; // "global" | "project" - the same rows render in both
2784
2785
  function skillRow(s) {
2785
2786
  const ver = s.version ? '<span class="tag">v' + esc(s.version) + "</span>" : "";
2787
+ // Project scope: same row, but the per-app chips toggle deployment INTO this project
2788
+ // (data-pskill, handled by the scope delegate) and the global-only controls are hidden.
2789
+ if (skillScope === "project") {
2790
+ const chips = (s.agentStates || []).map((a) =>
2791
+ '<button type="button" class="achip' + (a.deployed ? " on" : "") + '" data-pskill="' + esc(s.name) + '" data-pagent="' + esc(a.name) + '" data-poff="' + (a.deployed ? 1 : 0) + '" aria-label="' + esc(a.label) + '" title="'
2792
+ + (a.deployed ? esc(a.label) + " - deployed, click to remove" : esc(a.label) + " - not deployed, click to deploy") + '">' + (agentSvg(a.name) || esc(a.label)) + "</button>").join("");
2793
+ return '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(s.name) + ver + "</div>"
2794
+ + '<div class="set-hint">' + esc(s.description || "") + "</div>"
2795
+ + '<div class="set-agents">' + (chips || '<span class="tag">no agents</span>') + "</div></div></div>";
2796
+ }
2786
2797
  // Last-edited date (from skill.json), shown relative via the vendored <relative-time>.
2787
2798
  const edited = s.updated ? '<span class="tag" title="Last edited ' + esc(new Date(s.updated).toLocaleString()) + '">edited '
2788
2799
  + rtime(new Date(s.updated).getTime(), 'format="relative"', new Date(s.updated).toLocaleDateString()) + "</span>" : "";
@@ -2831,11 +2842,15 @@ export {
2831
2842
  let lastSkills = [];
2832
2843
  function renderSkills(skills) {
2833
2844
  lastSkills = skills; // kept so the editor can offer per-app save targets
2834
- const enigma = skills.filter((s) => s.source === "enigma");
2835
- const ext = skills.filter((s) => s.source === "external");
2836
2845
  const section = (title, blurb, list, emptyText) => '<div class="set-cat"><div class="set-cat-h">' + title + "</div>"
2837
2846
  + '<div class="set-cat-b">' + blurb + "</div>"
2838
2847
  + (list.length ? list.map(skillRow).join("") : '<div class="empty" style="padding:16px 0">' + emptyText + "</div>") + "</div>";
2848
+ if (skillScope === "project") {
2849
+ $("skillsBody").innerHTML = section("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.");
2850
+ return;
2851
+ }
2852
+ const enigma = skills.filter((s) => s.source === "enigma");
2853
+ const ext = skills.filter((s) => s.source === "external");
2839
2854
  $("skillsBody").innerHTML =
2840
2855
  section("Enigma skills", "Distributed and kept current by enigma. Disable to remove from your agents; enable to restore.", enigma, "No enigma skills.")
2841
2856
  + section("External skills", "Skills enigma did not author, found in your agents. Remove deletes the skill from every agent that has it.", ext, "No external skills found.");
@@ -2945,6 +2960,7 @@ export {
2945
2960
  let skillsLoaded = false;
2946
2961
  async function loadSkills() {
2947
2962
  if (isProjectScope()) return loadProjectSkills();
2963
+ skillScope = "global";
2948
2964
  loadSkillSettings();
2949
2965
  try {
2950
2966
  const res = await fetch("/api/skills", { cache: "no-store" });
@@ -3299,7 +3315,7 @@ export {
3299
3315
  const asSwitch = !c.choices || (c.choices.length === 2 && c.choices.indexOf(c.offChoice) !== -1);
3300
3316
  const ctl = !asSwitch
3301
3317
  ? '<select class="choice" data-proj-cfg="' + esc(c.key) + '">' + c.choices.map((x) => '<option value="' + esc(x) + '"' + (x === c.choice ? " selected" : "") + ">" + esc(prettyChoice(x)) + "</option>").join("") + "</select>"
3302
- : '<button type="button" class="toggle' + (c.value ? " on" : "") + '" data-proj-cfg-bool="' + esc(c.key) + '" data-on="' + (c.value ? 1 : 0) + '">' + (c.value ? "On" : "Off") + "</button>";
3318
+ : '<button type="button" class="toggle' + (c.value ? " on" : "") + '" data-kind="bool" data-proj-cfg-bool="' + esc(c.key) + '" data-on="' + (c.value ? 1 : 0) + '" title="' + (c.value ? "On" : "Off") + '">' + (c.value ? "On" : "Off") + "</button>";
3303
3319
  const reset = c.overridden ? ' <button type="button" class="iconbtn" data-proj-cfg-reset="' + esc(c.key) + '" title="Inherit global">reset</button>' : "";
3304
3320
  const badge = c.overridden ? ' <span class="tag">project</span>' : ' <span class="tag">inherited</span>';
3305
3321
  return '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(c.label) + badge + '</div><div class="set-hint">' + esc(c.hint) + "</div></div>"
@@ -3345,19 +3361,20 @@ export {
3345
3361
  } catch { setNote("settingsNote", "Could not reach the server.", "err"); }
3346
3362
  }
3347
3363
  async function loadProjectSkills() {
3364
+ skillScope = "project";
3348
3365
  const el = $("skillsBody");
3349
3366
  const ss = $("skillSettings"); if (ss) ss.innerHTML = '<div class="set-cat-b" style="padding:0 0 4px">Skill settings are global - switch Scope to Global to change them.</div>';
3350
3367
  el.innerHTML = '<div class="empty" style="padding:24px 0">Loading...</div>';
3351
3368
  let d; try { d = await fetchProjectDetail(); } catch { el.innerHTML = '<div class="empty" style="padding:24px 0">Project unavailable.</div>'; return; }
3352
3369
  if (d.error) { el.innerHTML = '<div class="empty" style="padding:24px 0">' + esc(d.error) + "</div>"; return; }
3353
- const deployed = new Set(); (d.agents || []).forEach((a) => (a.deployed || []).forEach((s) => deployed.add(s)));
3354
- const rows = (d.available || []).map((s) => {
3355
- const on = deployed.has(s.name);
3356
- return '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(s.name) + '</div><div class="set-hint">' + esc(s.description || "") + "</div></div>"
3357
- + '<div class="set-ctl"><button type="button" class="toggle' + (on ? " on" : "") + '" data-proj-skill="' + esc(s.name) + '" data-on="' + (on ? 1 : 0) + '">' + (on ? "On" : "Off") + "</button></div></div>";
3358
- }).join("") || '<div class="empty" style="padding:8px 0">No skills.</div>';
3359
- el.innerHTML = '<div class="scope-banner">Skills for <b>' + esc(d.label) + "</b> &middot; deploy or remove for this project</div>"
3360
- + '<div class="panel"><div class="set-grid">' + rows + "</div></div>";
3370
+ // Build the SAME skill objects renderSkills/skillRow use globally; agentStates carries
3371
+ // this project's per-app deployment so the rows render identically (chips per app).
3372
+ const agents = d.agents || [];
3373
+ const skills = (d.available || []).map((s) => ({
3374
+ name: s.name, description: s.description, source: "enigma",
3375
+ agentStates: agents.map((a) => ({ name: a.name, label: a.label, deployed: (a.deployed || []).indexOf(s.name) !== -1 })),
3376
+ }));
3377
+ renderSkills(skills);
3361
3378
  }
3362
3379
  async function projAction(payload, confirmMsg) {
3363
3380
  if (!isProjectScope()) return;
@@ -3382,6 +3399,8 @@ export {
3382
3399
  }
3383
3400
  function onProjectControlClick(e) {
3384
3401
  if (!isProjectScope()) return;
3402
+ const chip = e.target.closest("[data-pskill]"); // per-app deploy chip in the Skills view
3403
+ if (chip) { projAction({ op: "skill", name: chip.dataset.pskill, agent: chip.dataset.pagent, on: chip.dataset.poff !== "1" }); return; }
3385
3404
  const sk = e.target.closest("[data-proj-skill]");
3386
3405
  if (sk) { projAction({ op: "skill", name: sk.dataset.projSkill, on: sk.dataset.on !== "1" }); return; }
3387
3406
  const cb = e.target.closest("[data-proj-cfg-bool]");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enigmax/dashboard",
3
- "version": "0.1.47",
3
+ "version": "0.1.48",
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": [