File size: 2,056 Bytes
b694417
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import ForceGraph3D from "react-force-graph-3d";

import type { ExtractResponse } from "../types";
import { escapeHtml } from "./graphHtml";

interface GraphViewProps {
  result: ExtractResponse;
}

interface GraphNode {
  id: string;
  label: string;
  type: string;
  color: string;
}

interface GraphLink {
  source: string;
  target: string;
  label: string;
}

const entityColor = "#2DD4BF";
const relationColor = "#FB7185";

function toGraphData(result: ExtractResponse): {
  nodes: GraphNode[];
  links: GraphLink[];
} {
  const nodes = result.entities.map((entity, index) => ({
    id: String(index),
    label: result.tokens.slice(entity.start, entity.end).join(" "),
    type: entity.type,
    color: entityColor,
  }));
  const links = result.relations
    .filter(
      (relation) =>
        nodes[relation.head] !== undefined &&
        nodes[relation.tail] !== undefined,
    )
    .map((relation) => ({
      source: String(relation.head),
      target: String(relation.tail),
      label: relation.type.replaceAll("_", " "),
    }));
  return { nodes, links };
}

export default function GraphView({ result }: GraphViewProps) {
  const graphData = toGraphData(result);
  if (graphData.nodes.length === 0) {
    return (
      <section className="graph-empty">
        No entities were found in this text.
      </section>
    );
  }

  return (
    <section
      className="graph-canvas"
      aria-label="Interactive 3D entity relation graph"
    >
      <ForceGraph3D
        graphData={graphData}
        backgroundColor="#0B0F14"
        nodeLabel={(node) =>
          `<strong>${escapeHtml((node as GraphNode).label)}</strong><br/>${escapeHtml((node as GraphNode).type)}`
        }
        nodeColor={(node) => (node as GraphNode).color}
        nodeRelSize={5}
        linkColor={() => relationColor}
        linkWidth={1.5}
        linkDirectionalArrowLength={3.5}
        linkDirectionalArrowRelPos={1}
        linkLabel={(link) => escapeHtml((link as GraphLink).label)}
        showNavInfo={false}
      />
    </section>
  );
}