@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.
- package/dist/cli.js +21 -17
- 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
|
-
|
|
3733
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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"
|