Browse Source

Add node and link interfaces

master
limina1 1 year ago
parent
commit
5b790c1279
  1. 86
      src/lib/components/EventNetwork.svelte
  2. 6
      src/routes/visualize/+page.svelte

86
src/lib/components/EventNetwork.svelte

@ -5,14 +5,20 @@ @@ -5,14 +5,20 @@
export let events: NDKEvent[] = [];
let svg;
let svg: SVGSVGElement;
let isDarkMode = false;
const nodeRadius = 20;
const dragRadius = 45;
const linkDistance = 120;
let container: HTMLDivElement;
let width: number;
let height: number;
$: if (container) {
width = container.clientWidth || 800;
height = container.clientHeight || 600;
}
interface NetworkNode {
id: string;
event?: NDKEvent;
@ -24,58 +30,69 @@ @@ -24,58 +30,69 @@
type: "Index" | "Content";
}
function getEventColor(eventId: string): string {
const num = parseInt(eventId.slice(0, 4), 16);
const hue = num % 360;
const saturation = 70;
const lightness = 75;
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
interface NetworkLink {
source: NetworkNode;
target: NetworkNode;
isSequential: boolean;
}
function generateGraph(events: NDKEvent[]): [object[], object[]] {
const nodes = [];
const links = [];
const nodeMap = new Map();
function getNode(id: string, event?: NDKEvent, index?: number) {
function getNode(
id: string,
nodeMap: Map<string, NetworkNode>,
event?: NDKEvent,
index?: number,
): NetworkNode | null {
if (!id) return null;
if (!nodeMap.has(id)) {
const node = {
const node: NetworkNode = {
id,
event,
index,
isContainer: event?.kind === 30040,
title: event?.getMatchingTags("title")?.[0]?.[1] || "Untitled",
content: event?.content || "",
author: event?.pubkey,
author: event?.pubkey || "",
type: event?.kind === 30040 ? "Index" : "Content",
};
nodes.push(node);
nodeMap.set(id, node);
}
return nodeMap.get(id);
return nodeMap.get(id) || null;
}
function getEventColor(eventId: string): string {
const num = parseInt(eventId.slice(0, 4), 16);
const hue = num % 360;
const saturation = 70;
const lightness = 75;
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
}
function generateGraph(events: NDKEvent[]): {
nodes: NetworkNode[];
links: NetworkLink[];
} {
const nodes: NetworkNode[] = [];
const links: NetworkLink[] = [];
const nodeMap = new Map<string, NetworkNode>();
// Process index events first
const indexEvents = events.filter((e) => e.kind === 30040);
indexEvents.forEach((index) => {
if (!index.id) return;
const contentRefs = index.getMatchingTags("e");
const sourceNode = getNode(index.id, index);
const sourceNode = getNode(index.id, nodeMap, index);
if (!sourceNode) return;
nodes.push(sourceNode);
// Create a linear chain of content events
contentRefs.forEach((tag, idx) => {
if (!tag[1]) return;
const targetEvent = events.find((e) => e.id === tag[1]);
if (!targetEvent) return;
const targetNode = getNode(tag[1], targetEvent, idx);
const targetNode = getNode(tag[1], nodeMap, targetEvent, idx);
if (!targetNode) return;
nodes.push(targetNode);
const prevNodeId =
idx === 0 ? sourceNode.id : contentRefs[idx - 1]?.[1];
@ -93,7 +110,6 @@ @@ -93,7 +110,6 @@
return { nodes, links };
}
function drawNetwork() {
if (!svg || !events?.length) return;
@ -102,14 +118,7 @@ @@ -102,14 +118,7 @@
const { nodes, links } = generateGraph(events);
if (!nodes.length) return;
const svgElement = d3
.select(svg)
.attr(
"class",
"network-leather w-full border border-gray-300 dark:border-gray-700 rounded",
)
.attr("viewBox", [0, 0, width, height]);
const svgElement = d3.select(svg).attr("viewBox", `0 0 ${width} ${height}`);
// Set up zoom behavior
const zoom = d3
.zoom()
@ -124,19 +133,22 @@ @@ -124,19 +133,22 @@
// Force simulation setup
const simulation = d3
.forceSimulation(nodes)
.forceSimulation<NetworkNode>(nodes)
.force(
"link",
d3
.forceLink(links)
.forceLink<NetworkNode, NetworkLink>(links)
.id((d) => d.id)
.distance(linkDistance),
)
.force("charge", d3.forceManyBody().strength(-500))
.force("charge", d3.forceManyBody<NetworkNode>().strength(-500))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("x", d3.forceX(width / 2).strength(0.1))
.force("y", d3.forceY(height / 2).strength(0.1))
.force("collision", d3.forceCollide().radius(nodeRadius * 2.5));
.force("x", d3.forceX<NetworkNode>(width / 2).strength(0.1))
.force("y", d3.forceY<NetworkNode>(height / 2).strength(0.1))
.force(
"collision",
d3.forceCollide<NetworkNode>().radius(nodeRadius * 2.5),
);
// Define arrow marker with black fill
const marker = g
@ -322,7 +334,7 @@ @@ -322,7 +334,7 @@
});
});
const resizeObserver = new ResizeObserver((entries) => {
let resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
width = entry.contentRect.width;
height = entry.contentRect.height || width * 0.6;

6
src/routes/visualize/+page.svelte

@ -83,10 +83,6 @@ @@ -83,10 +83,6 @@
{:else}
<EventNetwork {events} />
<div class="mt-8 prose dark:prose-invert max-w-none">
<!-- Legend section with proper styling -->
<h2 class="h-leather">About This Visualization</h2>
<!-- ... rest of the content ... -->
</div>
<div class="mt-8 prose dark:prose-invert max-w-none"></div>
{/if}
</div>

Loading…
Cancel
Save