Packages

A Fair Source multi-agent runtime with deterministic agent scoring and replayable run history.

Retired package: Deprecated - superseded — operator console moved to the Syntropy app

Current section

Files

Jump to
syntropy priv static assets js app.js
Raw

priv/static/assets/js/app.js

import { Socket } from "/vendor/phoenix/phoenix.mjs";
import { LiveSocket } from "/vendor/phoenix_live_view/phoenix_live_view.esm.js";
const LatticeGraph = {
mounted() {
this.lastGraph = null;
this.renderGraph();
},
updated() {
this.renderGraph();
},
destroyed() {
this.lastGraph = null;
this.el.innerHTML = "";
},
renderGraph() {
const raw = this.el.dataset.graph;
if (!raw || raw === this.lastGraph) return;
const payload = this.readPayload(raw);
if (!payload) return;
const width = this.el.clientWidth || 960;
const height = 540;
const nodes = this.layoutNodes(payload.nodes, payload.edges, width, height);
this.el.innerHTML = "";
this.lastGraph = raw;
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
svg.setAttribute("role", "img");
svg.setAttribute("aria-label", `${payload.mode} lattice graph`);
this.renderBackdrop(svg, width, height);
this.renderEdges(svg, payload.edges, nodes);
this.renderNodes(svg, nodes);
this.el.appendChild(svg);
},
readPayload(raw) {
try {
return JSON.parse(raw);
} catch (_error) {
return null;
}
},
renderBackdrop(svg, width, height) {
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
group.setAttribute("class", "graph-backdrop");
const axis = document.createElementNS("http://www.w3.org/2000/svg", "line");
axis.setAttribute("x1", width * 0.08);
axis.setAttribute("y1", height * 0.5);
axis.setAttribute("x2", width * 0.92);
axis.setAttribute("y2", height * 0.5);
axis.setAttribute("stroke", "rgba(148, 163, 184, 0.10)");
axis.setAttribute("stroke-width", "1");
group.appendChild(axis);
svg.appendChild(group);
},
renderEdges(svg, edges, nodes) {
const positions = new Map(nodes.map((node) => [node.id, node]));
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
group.setAttribute("class", "graph-edges");
edges.forEach((edge) => {
const source = positions.get(edge.source);
const target = positions.get(edge.target);
if (!source || !target) return;
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
line.setAttribute("x1", source.x);
line.setAttribute("y1", source.y);
line.setAttribute("x2", target.x);
line.setAttribute("y2", target.y);
line.setAttribute("stroke", "#8fc3ff");
line.setAttribute("stroke-width", String(1.5 + edge.weight * 5));
line.setAttribute("stroke-opacity", String(Math.max(0.28, edge.weight)));
group.appendChild(line);
});
svg.appendChild(group);
},
renderNodes(svg, nodes) {
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
group.setAttribute("class", "graph-nodes");
nodes.forEach((node) => {
const wrapper = document.createElementNS("http://www.w3.org/2000/svg", "g");
wrapper.setAttribute("transform", `translate(${node.x}, ${node.y})`);
wrapper.setAttribute("class", "graph-node");
wrapper.setAttribute("tabindex", "0");
wrapper.setAttribute("role", "button");
wrapper.setAttribute(
"aria-label",
`Focus ${node.name}, ${node.perspective} perspective, position ${node.position.toFixed(3)}`
);
const halo = document.createElementNS("http://www.w3.org/2000/svg", "circle");
halo.setAttribute("fill", "none");
halo.setAttribute("r", String(node.radius + 8));
halo.setAttribute("stroke", node.current ? "#f8fafc" : "rgba(143, 195, 255, 0.28)");
halo.setAttribute("stroke-width", node.current ? "2" : "1");
wrapper.appendChild(halo);
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("fill", this.nodeFill(node));
circle.setAttribute(
"stroke",
node.current ? "#f8fafc" : node.selected ? "#7dd3fc" : "rgba(203, 213, 225, 0.74)"
);
circle.setAttribute("stroke-width", node.current ? "4" : node.selected ? "3" : "2");
circle.setAttribute("r", String(node.radius));
if (node.temporary) {
circle.setAttribute("stroke-dasharray", "5 4");
}
wrapper.appendChild(circle);
const shortLabel = document.createElementNS("http://www.w3.org/2000/svg", "text");
shortLabel.setAttribute("text-anchor", "middle");
shortLabel.setAttribute("dy", "0.35em");
shortLabel.setAttribute("fill", "#f8fafc");
shortLabel.setAttribute("font-size", "18");
shortLabel.setAttribute("font-weight", "800");
shortLabel.textContent = this.shortNodeLabel(node.name);
wrapper.appendChild(shortLabel);
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
label.setAttribute("text-anchor", "middle");
label.setAttribute("dy", String(node.radius + 22));
label.setAttribute("fill", "#dbe6ff");
label.setAttribute("font-size", "14");
label.setAttribute("font-weight", "700");
label.textContent = node.name;
wrapper.appendChild(label);
const sublabel = document.createElementNS("http://www.w3.org/2000/svg", "text");
sublabel.setAttribute("text-anchor", "middle");
sublabel.setAttribute("dy", String(node.radius + 40));
sublabel.setAttribute("fill", "rgba(219, 230, 255, 0.72)");
sublabel.setAttribute("font-size", "12");
sublabel.textContent = `${node.perspective} · ${node.position.toFixed(3)}`;
wrapper.appendChild(sublabel);
const focus = () => this.pushEvent("focus_agent", { id: node.id });
wrapper.addEventListener("click", focus);
wrapper.addEventListener("keydown", (event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
focus();
}
});
group.appendChild(wrapper);
});
svg.appendChild(group);
},
layoutNodes(nodes, edges, width, height) {
const positioned = nodes.map((node, index) => ({
...node,
radius: 28 + Math.min(Math.max(node.position, 0), 1) * 12,
x: width / 2 + Math.cos((index / Math.max(nodes.length, 1)) * Math.PI * 2) * width * 0.28,
y: height / 2 + Math.sin((index / Math.max(nodes.length, 1)) * Math.PI * 2) * height * 0.25,
vx: 0,
vy: 0,
}));
const byId = new Map(positioned.map((node) => [node.id, node]));
for (let tick = 0; tick < 60; tick += 1) {
positioned.forEach((node) => {
node.vx *= 0.88;
node.vy *= 0.88;
});
for (let i = 0; i < positioned.length; i += 1) {
for (let j = i + 1; j < positioned.length; j += 1) {
const left = positioned[i];
const right = positioned[j];
const dx = right.x - left.x;
const dy = right.y - left.y;
const distance = Math.max(70, Math.hypot(dx, dy));
const force = 2800 / (distance * distance);
const fx = (dx / distance) * force;
const fy = (dy / distance) * force;
left.vx -= fx;
left.vy -= fy;
right.vx += fx;
right.vy += fy;
}
}
edges.forEach((edge) => {
const source = byId.get(edge.source);
const target = byId.get(edge.target);
if (!source || !target) return;
const dx = target.x - source.x;
const dy = target.y - source.y;
const distance = Math.max(1, Math.hypot(dx, dy));
const desired = 150 - edge.weight * 35;
const strength = (distance - desired) * 0.0025;
const fx = (dx / distance) * strength;
const fy = (dy / distance) * strength;
source.vx += fx;
source.vy += fy;
target.vx -= fx;
target.vy -= fy;
});
positioned.forEach((node) => {
node.vx += (width / 2 - node.x) * 0.0009;
node.vy += (height / 2 - node.y) * 0.0009;
node.x = Math.min(width - 80, Math.max(80, node.x + node.vx * 6));
node.y = Math.min(height - 85, Math.max(65, node.y + node.vy * 6));
});
}
return positioned;
},
nodeFill(node) {
if (node.current) return "#1f3f56";
if (node.selected) return "#12324a";
if (node.active) return "#102d3d";
return "#0b1626";
},
shortNodeLabel(name) {
return name
.split(/\s+/)
.filter(Boolean)
.slice(0, 2)
.map((part) => part[0])
.join("")
.toUpperCase();
},
};
const csrfToken = document
.querySelector("meta[name='csrf-token']")
?.getAttribute("content");
const Hooks = {
LatticeGraph,
};
const liveSocket = new LiveSocket("/live", Socket, {
hooks: Hooks,
params: csrfToken ? { _csrf_token: csrfToken } : {},
});
liveSocket.connect();
window.liveSocket = liveSocket;