feat: add self to graph

This commit is contained in:
2026-04-05 00:39:32 +02:00
parent 914945279f
commit 7838760ca4

View File

@@ -105,10 +105,13 @@ function buildGraphArrays(
theme: ReturnType<typeof getThemeStatusColors>, theme: ReturnType<typeof getThemeStatusColors>,
): BuiltGraph { ): BuiltGraph {
const interfaces = rawNodes.filter(e => e.type === "interface").sort((a, b) => a.name.localeCompare(b.name)); const interfaces = rawNodes.filter(e => e.type === "interface").sort((a, b) => a.name.localeCompare(b.name));
const selfNode = rawNodes.find(e => e.is_self && e.type !== "interface");
const peers = rawNodes.filter(e => !e.is_self && e.type !== "interface"); const peers = rawNodes.filter(e => !e.is_self && e.type !== "interface");
// Only peers are points — interfaces are clusters, not nodes // Peers + self node are points — interfaces are clusters, not nodes
const entries = peers; // Self node goes first so it's easy to find by index
const entries = selfNode ? [selfNode, ...peers] : peers;
const selfIndex = selfNode ? 0 : -1;
const n = entries.length; const n = entries.length;
const nClusters = interfaces.length; const nClusters = interfaces.length;
const hashToIndex = new Map<string, number>(); const hashToIndex = new Map<string, number>();
@@ -119,11 +122,13 @@ function buildGraphArrays(
const clusterStrength = new Float32Array(n); const clusterStrength = new Float32Array(n);
// Cluster map: interface name → cluster index // Cluster map: interface name → cluster index
// Self node gets its own cluster at the end
const clusterMap = new Map<string, number>(); const clusterMap = new Map<string, number>();
interfaces.forEach((iface, i) => clusterMap.set(iface.name, i)); interfaces.forEach((iface, i) => clusterMap.set(iface.name, i));
const selfClusterIndex = interfaces.length;
// Cluster names for labels // Cluster names for labels
const clusterNames = interfaces.map(i => i.name); const clusterNames = [...interfaces.map(i => i.name), ...(selfNode ? [selfNode.name] : [])];
// No explicit cluster positions — let cosmos use centermass // No explicit cluster positions — let cosmos use centermass
const clusterPositions: (number | undefined)[] = []; const clusterPositions: (number | undefined)[] = [];
@@ -141,8 +146,10 @@ function buildGraphArrays(
positions[i * 2 + 1] = CENTER + (Math.random() - 0.5) * SPACE_SIZE * 0.5; positions[i * 2 + 1] = CENTER + (Math.random() - 0.5) * SPACE_SIZE * 0.5;
} }
// Colors — by status // Colors — self node uses bright white, others by status
const rgba = statusRGBA(entry, theme); const rgba = i === selfIndex
? [1, 1, 1, 1] as [number, number, number, number]
: statusRGBA(entry, theme);
colors[i * 4 + 0] = rgba[0]; colors[i * 4 + 0] = rgba[0];
colors[i * 4 + 1] = rgba[1]; colors[i * 4 + 1] = rgba[1];
colors[i * 4 + 2] = rgba[2]; colors[i * 4 + 2] = rgba[2];
@@ -150,11 +157,15 @@ function buildGraphArrays(
sizes[i] = 3; sizes[i] = 3;
// Cluster assignment — map to parent interface // Cluster assignment — self node gets its own cluster, peers map to parent interface
if (i === selfIndex) {
clusterIndices.push(selfClusterIndex);
} else {
const pi = peers.indexOf(entry); const pi = peers.indexOf(entry);
const parentHash = findParentIface(entry, interfaces, pi); const parentHash = findParentIface(entry, interfaces, pi);
const parentName = parentHash ? interfaces.find(f => f.hash === parentHash)?.name : undefined; const parentName = parentHash ? interfaces.find(f => f.hash === parentHash)?.name : undefined;
clusterIndices.push(parentName !== undefined ? clusterMap.get(parentName) : undefined); clusterIndices.push(parentName !== undefined ? clusterMap.get(parentName) : undefined);
}
// Cluster strength // Cluster strength
clusterStrength[i] = nClusters > 1 ? (nClusters - (i % nClusters)) / nClusters : 1; clusterStrength[i] = nClusters > 1 ? (nClusters - (i % nClusters)) / nClusters : 1;
@@ -263,14 +274,16 @@ export default function BrowseView() {
div.style.pointerEvents = "none"; div.style.pointerEvents = "none";
div.style.whiteSpace = "nowrap"; div.style.whiteSpace = "nowrap";
div.style.transform = "translate(-50%, -100%)"; div.style.transform = "translate(-50%, -100%)";
div.style.padding = "2px 8px"; div.style.padding = "0";
div.style.borderRadius = "4px"; div.style.background = "none";
div.style.background = "var(--popover)"; div.style.border = "none";
div.style.border = "1px solid var(--border)";
div.style.color = "var(--foreground)"; div.style.color = "var(--foreground)";
div.style.fontFamily = "JetBrains Mono, monospace"; div.style.fontFamily = "JetBrains Mono, monospace";
div.style.fontSize = "11px"; div.style.fontSize = "14px";
div.style.boxShadow = "0 2px 8px rgba(0,0,0,0.3)"; div.style.fontWeight = "700";
div.style.letterSpacing = "0.05em";
div.style.textTransform = "uppercase";
div.style.opacity = "0.85";
div.textContent = names[i] ?? ""; div.textContent = names[i] ?? "";
container.appendChild(div); container.appendChild(div);
labelDivsRef.current.push(div); labelDivsRef.current.push(div);