@intentius/behold 0.4.0 → 0.4.1

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/dist/cli.js +21 -17
  2. package/package.json +3 -1
package/dist/cli.js CHANGED
@@ -3729,17 +3729,19 @@ function safeJson(value) {
3729
3729
  return JSON.stringify(value).replace(/</g, "\\u003c");
3730
3730
  }
3731
3731
  var LANES_CSS = `
3732
- #behold-lanes { position: fixed; left: 0; right: 0; bottom: 0; background: #0d1117;
3733
- border-top: 1px solid #30363d; padding: 8px 12px 10px; font: 12px ui-sans-serif, system-ui, sans-serif; color: #8b949e; }
3732
+ body { background: var(--bg, #0d1117); color: var(--fg, #e6edf3); padding-bottom: 150px; }
3733
+ #behold-lanes { position: fixed; left: 0; right: 0; bottom: 0; background: var(--bg, #0d1117);
3734
+ border-top: 1px solid var(--line, #30363d); padding: 8px 12px 10px; font: 12px ui-sans-serif, system-ui, sans-serif; color: var(--muted, #8b949e); }
3734
3735
  #behold-lanes .hd { display: flex; gap: 14px; align-items: baseline; margin-bottom: 4px; }
3735
- #behold-lanes .hd .rt { color: #d29922; }
3736
+ #behold-lanes .hd .rt { color: var(--foreign, #d29922); }
3737
+ #behold-lanes .hd a { color: var(--pending, #58a6ff); text-decoration: none; margin-left: auto; }
3736
3738
  #behold-lanes canvas { display: block; width: 100%; cursor: pointer; }
3737
3739
  #behold-diff { position: fixed; right: 12px; bottom: 156px; width: 260px; max-height: 40vh; overflow: auto;
3738
- background: #161b22; border: 1px solid #30363d; border-radius: 6px; padding: 10px 12px; font: 12px ui-sans-serif, system-ui, sans-serif;
3739
- color: #e6edf3; display: none; }
3740
- #behold-diff h4 { margin: 0 0 6px; font-size: 12px; color: #8b949e; }
3741
- #behold-diff .a { color: #3fb950; } #behold-diff .r { color: #f85149; } #behold-diff .c { color: #d29922; }
3742
- body { padding-bottom: 150px; }`;
3740
+ background: var(--panel, #161b22); border: 1px solid var(--line, #30363d); border-radius: 6px; padding: 10px 12px; font: 12px ui-sans-serif, system-ui, sans-serif;
3741
+ color: var(--fg, #e6edf3); display: none; }
3742
+ #behold-diff h4 { margin: 0 0 6px; font-size: 12px; color: var(--muted, #8b949e); }
3743
+ #behold-diff .a { color: var(--managed, #3fb950); } #behold-diff .r { color: var(--degraded, #f85149); } #behold-diff .c { color: var(--foreign, #d29922); }`;
3744
+ var THEME_BOOT = `<script type="module">import { initTheme, onThemeChange, mountThemePicker } from "/theme.js"; initTheme(); mountThemePicker(document.getElementById("behold-lanes-pickers")); onThemeChange(() => window.dispatchEvent(new Event("behold-theme")));</script>`;
3743
3745
  function laneStripScript(frames) {
3744
3746
  return `<script>
3745
3747
  const LF = ${safeJson(frames)};
@@ -3750,6 +3752,7 @@ const LF = ${safeJson(frames)};
3750
3752
  const rowH = 22, padL = 96, padR = 16, padT = 6;
3751
3753
  const H = padT + subs.length * rowH + 24;
3752
3754
  const t0 = LF[0].t, tN = LF[LF.length - 1].t, span = Math.max(1, tN - t0);
3755
+ const css = (n, fb) => (getComputedStyle(document.documentElement).getPropertyValue(n).trim() || fb);
3753
3756
  const offset = {}; // per-substrate time offset (graph-inert)
3754
3757
  let cur = LF.length - 1, focus = null, pair = null;
3755
3758
 
@@ -3763,7 +3766,7 @@ const LF = ${safeJson(frames)};
3763
3766
  const inNow = now !== undefined, inPrev = prev !== undefined;
3764
3767
  return inNow !== inPrev || (inNow && inPrev && now !== prev);
3765
3768
  }
3766
- const color = s => s === "good" ? "#3fb950" : s === "warn" ? "#d29922" : s === "accent" ? "#58a6ff" : "#6e7681";
3769
+ const color = s => s === "good" ? css("--managed", "#3fb950") : s === "warn" ? css("--foreign", "#d29922") : s === "accent" ? css("--pending", "#58a6ff") : css("--edge", "#6e7681");
3767
3770
 
3768
3771
  function draw() {
3769
3772
  const dpr = window.devicePixelRatio || 1;
@@ -3772,22 +3775,22 @@ const LF = ${safeJson(frames)};
3772
3775
  c.clearRect(0, 0, host.clientWidth, H); c.font = "12px ui-sans-serif, system-ui, sans-serif";
3773
3776
  subs.forEach((s, r) => {
3774
3777
  const y = padT + r * rowH + rowH / 2;
3775
- c.fillStyle = offset[s] ? "#d29922" : "#8b949e"; c.textAlign = "left"; c.fillText(s, 8, y + 4);
3776
- c.strokeStyle = "#21262d"; c.beginPath(); c.moveTo(padL, y); c.lineTo(host.clientWidth - padR, y); c.stroke();
3778
+ c.fillStyle = offset[s] ? css("--foreign", "#d29922") : css("--muted", "#8b949e"); c.textAlign = "left"; c.fillText(s, 8, y + 4);
3779
+ c.strokeStyle = css("--line", "#21262d"); c.beginPath(); c.moveTo(padL, y); c.lineTo(host.clientWidth - padR, y); c.stroke();
3777
3780
  LF.forEach((f, i) => {
3778
3781
  if (!f.byLexicon[s]) return;
3779
3782
  // dot per substrate; brighter when a node in this substrate changed at i
3780
3783
  const changed = Object.keys(f.status).some(id => f.lexicon[id] === s && changedAt(id, i));
3781
3784
  const hi = focus && f.lexicon[focus] === s && changedAt(focus, i);
3782
- c.fillStyle = hi ? "#f0f6fc" : changed ? color(mode(f, s)) : "#30363d";
3785
+ c.fillStyle = hi ? css("--fg", "#f0f6fc") : changed ? color(mode(f, s)) : css("--line", "#30363d");
3783
3786
  c.beginPath(); c.arc(xOf(i, s), y, i === cur ? 5 : hi ? 4.5 : 3.5, 0, 7); c.fill();
3784
3787
  });
3785
3788
  });
3786
- const px = baseX(cur); c.strokeStyle = "#58a6ff"; c.lineWidth = 1.5;
3789
+ const px = baseX(cur); c.strokeStyle = css("--pending", "#58a6ff"); c.lineWidth = 1.5;
3787
3790
  c.beginPath(); c.moveTo(px, padT - 2); c.lineTo(px, padT + subs.length * rowH); c.stroke();
3788
- if (pair != null) { const qx = baseX(pair); c.strokeStyle = "#d29922"; c.setLineDash([3,3]);
3791
+ if (pair != null) { const qx = baseX(pair); c.strokeStyle = css("--foreign", "#d29922"); c.setLineDash([3,3]);
3789
3792
  c.beginPath(); c.moveTo(qx, padT - 2); c.lineTo(qx, padT + subs.length * rowH); c.stroke(); c.setLineDash([]); }
3790
- c.fillStyle = "#e6edf3"; c.textAlign = "center"; c.fillText(LF[cur].name, px, padT + subs.length * rowH + 16);
3793
+ c.fillStyle = css("--fg", "#e6edf3"); c.textAlign = "center"; c.fillText(LF[cur].name, px, padT + subs.length * rowH + 16);
3791
3794
  document.getElementById("behold-lanes-meta").textContent =
3792
3795
  LF.length + " frames \xB7 frame " + (cur + 1) + "/" + LF.length + (focus ? " \xB7 focus " + focus : "");
3793
3796
  document.getElementById("behold-lanes-rt").style.display = anyOffset() ? "inline" : "none";
@@ -3828,6 +3831,7 @@ const LF = ${safeJson(frames)};
3828
3831
  window.addEventListener("keydown", (e) => { if (e.key === "ArrowLeft") go(cur - 1); if (e.key === "ArrowRight") go(cur + 1);
3829
3832
  if (e.key === "Escape") { focus = null; pair = null; showDiff(); draw(); } });
3830
3833
  window.addEventListener("resize", draw);
3834
+ window.addEventListener("behold-theme", draw); // #198: theme flip repaints the strip
3831
3835
 
3832
3836
  // focus cursor: click a graph node \u2192 highlight where it changed (graph \u2192 lanes)
3833
3837
  function wireNodes() { document.querySelectorAll("[data-node-id]").forEach(el => {
@@ -3853,7 +3857,7 @@ function renderLanes(frames, summaries) {
3853
3857
  status: Object.fromEntries(f.ir.nodes.map((n) => [n.id, n.attrs?._status ?? ""])),
3854
3858
  lexicon: Object.fromEntries(f.ir.nodes.map((n) => [n.id, n.lexicon]))
3855
3859
  }));
3856
- const strip = `<style>${LANES_CSS}</style><div id="behold-diff"></div><div id="behold-lanes"><div class="hd"><span>deployment lanes</span><span id="behold-lanes-meta"></span><span class="rt" id="behold-lanes-rt" style="display:none">offset \u2014 graph shows real time</span></div><canvas id="behold-lanes-canvas"></canvas></div>` + laneStripScript(laneFrames);
3860
+ const strip = `<style>${LANES_CSS}</style>` + THEME_BOOT + `<div id="behold-diff"></div><div id="behold-lanes"><div class="hd"><span>deployment lanes</span><span id="behold-lanes-meta"></span><span class="rt" id="behold-lanes-rt" style="display:none">offset \u2014 graph shows real time</span><a href="/">\u2190 graph</a><span id="behold-lanes-pickers"></span></div><canvas id="behold-lanes-canvas"></canvas></div>` + laneStripScript(laneFrames);
3857
3861
  return doc.includes("</body>") ? doc.replace("</body>", `${strip}</body>`) : doc + strip;
3858
3862
  }
3859
3863
 
@@ -4009,7 +4013,7 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
4009
4013
  const all = frames.all();
4010
4014
  if (all.length < 2) {
4011
4015
  return c.html(
4012
- `<!doctype html><meta charset=utf-8><body style="font:14px system-ui;background:#0d1117;color:#8b949e;padding:2rem"><h3 style="color:#e6edf3">deployment lanes</h3><p>${all.length} frame(s) captured \u2014 need at least two to scrub.</p><p>Frames accrue when the estate moves: hit <b style="color:#e6edf3">\u21BB Refresh</b> (captures the current live state), run a <b style="color:#e6edf3">Sync</b>/Adopt, edit the source, or serve with <code>--poll</code> against a moving environment. Then reload.</p><p><a href="/" style="color:#58a6ff;text-decoration:none">\u2190 back to the graph</a></p></body>`
4016
+ `<!doctype html><meta charset=utf-8><script type="module">import { initTheme } from "/theme.js"; initTheme();</script><body style="font:14px system-ui;background:var(--bg,#0d1117);color:var(--muted,#8b949e);padding:2rem"><h3 style="color:var(--fg,#e6edf3)">deployment lanes</h3><p>${all.length} frame(s) captured \u2014 need at least two to scrub.</p><p>Frames accrue when the estate moves: hit <b style="color:var(--fg,#e6edf3)">\u21BB Refresh</b> (captures the current live state), run a <b style="color:var(--fg,#e6edf3)">Sync</b>/Adopt, edit the source, or serve with <code>--poll</code> against a moving environment. Then reload.</p><p><a href="/" style="color:var(--pending,#58a6ff);text-decoration:none">\u2190 back to the graph</a></p></body>`
4013
4017
  );
4014
4018
  }
4015
4019
  return c.html(renderLanes(all, frames.summaries()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/behold",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "description": "behold — a live control plane on chant. See your whole estate (every substrate in one graph), coloured by drift; act through delegated, gated Ops.",
6
6
  "bin": {
@@ -21,6 +21,7 @@
21
21
  "dev": "tsx src/cli.ts",
22
22
  "demo": "npm --prefix example-writes install && tsx src/cli.ts serve example-writes --local --env prod",
23
23
  "demo:k8s": "bash example-k8s/scripts/local/local-up.sh; npm --prefix example-k8s install && tsx src/cli.ts serve example-k8s --local --env local; bash example-k8s/scripts/local/local-down.sh",
24
+ "smoke:ui": "node smoke/ui-smoke.mjs",
24
25
  "tsc": "tsc --noEmit",
25
26
  "test": "vitest run",
26
27
  "build": "esbuild src/cli.ts --bundle --platform=node --format=esm --external:hono --external:@hono/node-server --external:@intentius/pinhole --outfile=dist/cli.js && chmod +x dist/cli.js",
@@ -36,6 +37,7 @@
36
37
  "devDependencies": {
37
38
  "@types/node": "^22.0.0",
38
39
  "esbuild": "^0.28.0",
40
+ "playwright": "^1.62.1",
39
41
  "tsx": "^4.19.0",
40
42
  "typescript": "^5.9.3",
41
43
  "vitest": "^4.1.9"