temporal97
Integrations

vis-network

Drive a live, physics-simulated network visualization directly from temporal97 Deltas.

vis-network renders interactive network diagrams with built-in physics, pan/zoom, and click events. Its DataSet API is built for incremental updates — which maps directly onto temporal97's Delta.

Installation

npm install vis-network vis-data

Setup

Create DataSet instances and wire them to a Network. The datasets hold the live state; the network just renders them.

import { DataSet } from 'vis-data';
import { Network } from 'vis-network';
import { TemporalGraph, type Delta } from 'temporal97';

type NodeAttrs = { label: string };
type EdgeAttrs = { source: string; target: string };

const temporal = new TemporalGraph<NodeAttrs, EdgeAttrs>();

const nodes = new DataSet<{ id: string; label: string }>();
const edges = new DataSet<{ id: string; from: string; to: string }>();

const network = new Network(
  document.getElementById('network')!,
  { nodes, edges },
  { physics: { stabilization: false } },
);

Syncing with Delta

function syncDelta(delta: Delta) {
  nodes.remove([...delta.nodes.removed]);
  edges.remove([...delta.edges.removed]);

  for (const id of delta.nodes.added) {
    const n = temporal.getNode(id)!;
    nodes.add({ id, label: n.label });
  }
  for (const id of delta.edges.added) {
    const e = temporal.getEdge(id)!;
    edges.add({ id, from: e.source, to: e.target });
  }

  for (const id of delta.nodes.updated) {
    const n = temporal.getNode(id)!;
    nodes.update({ id, label: n.label });
  }
  for (const id of delta.edges.updated) {
    const e = temporal.getEdge(id)!;
    edges.update({ id, from: e.source, to: e.target });
  }
}

Every temporal97 operation now feeds the visualization automatically:

syncDelta(temporal.append({
  snapshot: 0,
  mutations: [
    { kind: 'node', op: 'set', id: 'alice', value: { label: 'Alice' } },
    { kind: 'node', op: 'set', id: 'bob',   value: { label: 'Bob' } },
    { kind: 'edge', op: 'set', id: 'alice->bob', value: { source: 'alice', target: 'bob' } },
  ],
}));

syncDelta(temporal.append({
  snapshot: 1,
  mutations: [
    { kind: 'node', op: 'set', id: 'charlie', value: { label: 'Charlie' } },
    { kind: 'edge', op: 'set', id: 'bob->charlie', value: { source: 'bob', target: 'charlie' } },
  ],
}));

// Charlie and his edge disappear from the canvas automatically
syncDelta(temporal.rewind(0));

Building a timeline scrubber

Use getSnapshotIds() with a range input to let users drag through history:

const snapshotIds = temporal.getSnapshotIds();

const slider = document.getElementById('slider') as HTMLInputElement;
slider.min   = '0';
slider.max   = String(snapshotIds.length - 1);
slider.value = String(snapshotIds.length - 1);

slider.addEventListener('input', () => {
  syncDelta(temporal.seekTo(snapshotIds[Number(slider.value)]));
});

On this page