@aiready/visualizer 0.7.5 → 0.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,17 @@ interface GraphCanvasProps {
17
17
  onNodeClick: (node: FileNode | null) => void;
18
18
  }
19
19
 
20
+ interface D3Node extends d3.SimulationNodeDatum, FileNode {
21
+ fx?: number | null;
22
+ fy?: number | null;
23
+ }
24
+
25
+ interface D3Link extends d3.SimulationLinkDatum<D3Node> {
26
+ source: D3Node;
27
+ target: D3Node;
28
+ type: string;
29
+ }
30
+
20
31
  export function GraphCanvas({
21
32
  data,
22
33
  dimensions,
@@ -42,18 +53,18 @@ export function GraphCanvas({
42
53
  const svgGroup = svg.append('g');
43
54
 
44
55
  // Setup zoom
45
- const zoom = (d3 as any)
46
- .zoom()
56
+ const zoom = d3
57
+ .zoom<SVGSVGElement, unknown>()
47
58
  .scaleExtent([GRAPH_CONFIG.zoomMin, GRAPH_CONFIG.zoomMax])
48
- .on('zoom', (event: any) => {
49
- svgGroup.attr('transform', event.transform);
59
+ .on('zoom', (event: d3.D3ZoomEvent<SVGSVGElement, unknown>) => {
60
+ svgGroup.attr('transform', event.transform.toString());
50
61
  zoomTransformRef.current = event.transform;
51
62
  });
52
63
  svg.call(zoom);
53
64
  svg.call(zoom.transform, zoomTransformRef.current);
54
65
 
55
66
  // Prepare nodes and links
56
- const nodes = data.nodes.map((d, i) => {
67
+ const nodes: D3Node[] = data.nodes.map((d, i) => {
57
68
  // Initialize nodes in a circle around center with slight random spread
58
69
  const angle = (i / data.nodes.length) * Math.PI * 2;
59
70
  const radius = 50 + Math.random() * 30;
@@ -63,22 +74,28 @@ export function GraphCanvas({
63
74
  y: height / 2 + Math.sin(angle) * radius,
64
75
  };
65
76
  });
66
- const links = data.edges.map((d) => ({ ...d }));
77
+ const links: D3Link[] = data.edges.map((d) => {
78
+ // Find source and target nodes to satisfy D3Link interface
79
+ const source = nodes.find((n) => n.id === d.source) || nodes[0];
80
+ const target = nodes.find((n) => n.id === d.target) || nodes[0];
81
+ return {
82
+ ...d,
83
+ source,
84
+ target,
85
+ type: d.type || 'default',
86
+ };
87
+ });
67
88
 
68
89
  // Create force simulation
69
90
  const simulation = d3
70
- .forceSimulation(nodes as d3.SimulationNodeDatum[])
91
+ .forceSimulation<D3Node>(nodes)
71
92
  .force(
72
93
  'link',
73
94
  d3
74
- .forceLink(links)
75
- .id((d: unknown) => (d as { id: string }).id)
76
- .distance((d: unknown) =>
77
- getEdgeDistance((d as { type: string }).type)
78
- )
79
- .strength((d: unknown) =>
80
- getEdgeStrength((d as { type: string }).type)
81
- )
95
+ .forceLink<D3Node, D3Link>(links)
96
+ .id((d) => d.id)
97
+ .distance((d) => getEdgeDistance(d.type))
98
+ .strength((d) => getEdgeStrength(d.type))
82
99
  )
83
100
  .force(
84
101
  'charge',
@@ -87,15 +104,15 @@ export function GraphCanvas({
87
104
  .force('center', d3.forceCenter(width / 2, height / 2))
88
105
  .force(
89
106
  'collision',
90
- d3.forceCollide().radius(GRAPH_CONFIG.collisionRadius)
107
+ d3.forceCollide<D3Node>().radius(GRAPH_CONFIG.collisionRadius)
91
108
  )
92
109
  .force(
93
110
  'x',
94
- d3.forceX(width / 2).strength(GRAPH_CONFIG.simulation.centerStrength)
111
+ d3.forceX<D3Node>(width / 2).strength(GRAPH_CONFIG.simulation.centerStrength)
95
112
  )
96
113
  .force(
97
114
  'y',
98
- d3.forceY(height / 2).strength(GRAPH_CONFIG.simulation.centerStrength)
115
+ d3.forceY<D3Node>(height / 2).strength(GRAPH_CONFIG.simulation.centerStrength)
99
116
  );
100
117
 
101
118
  // Create link group
@@ -105,17 +122,9 @@ export function GraphCanvas({
105
122
  .data(links)
106
123
  .enter()
107
124
  .append('line')
108
- .attr(
109
- 'stroke',
110
- (d: unknown) =>
111
- edgeColors[(d as { type: string }).type] || edgeColors.default
112
- )
113
- .attr('stroke-opacity', (d: unknown) =>
114
- getEdgeOpacity((d as { type: string }).type)
115
- )
116
- .attr('stroke-width', (d: unknown) =>
117
- getEdgeStrokeWidth((d as { type: string }).type)
118
- );
125
+ .attr('stroke', (d) => edgeColors[d.type] || edgeColors.default)
126
+ .attr('stroke-opacity', (d) => getEdgeOpacity(d.type))
127
+ .attr('stroke-width', (d) => getEdgeStrokeWidth(d.type));
119
128
 
120
129
  // Create node group
121
130
  const nodeGroup = svgGroup.append('g').attr('class', 'nodes');
@@ -127,42 +136,27 @@ export function GraphCanvas({
127
136
  .append('g')
128
137
  .attr('cursor', 'pointer')
129
138
  .call(
130
- (d3 as any)
131
- .drag()
139
+ d3
140
+ .drag<SVGGElement, D3Node>()
132
141
  .on('start', dragstarted)
133
142
  .on('drag', dragged)
134
- .on('end', dragended) as any
143
+ .on('end', dragended)
135
144
  );
136
145
 
137
146
  // Add circles to nodes
138
147
  node
139
148
  .append('circle')
140
- .attr(
141
- 'r',
142
- (d: unknown) =>
143
- Math.sqrt((d as { value: number }).value || 10) +
144
- GRAPH_CONFIG.nodeBaseRadius
145
- )
146
- .attr(
147
- 'fill',
148
- (d: unknown) => (d as { color: string }).color || severityColors.default
149
- )
149
+ .attr('r', (d) => Math.sqrt(d.value || 10) + GRAPH_CONFIG.nodeBaseRadius)
150
+ .attr('fill', (d) => d.color || severityColors.default)
150
151
  .attr('stroke', effectiveTheme === 'dark' ? '#fff' : '#000')
151
152
  .attr('stroke-width', 1.5);
152
153
 
153
154
  // Add labels to nodes
154
155
  node
155
156
  .append('text')
156
- .text(
157
- (d: unknown) =>
158
- (d as { label: string }).label.split('/').pop() ||
159
- (d as { label: string }).label
160
- )
157
+ .text((d) => d.label.split('/').pop() || d.label)
161
158
  .attr('x', 0)
162
- .attr(
163
- 'y',
164
- (d: unknown) => Math.sqrt((d as { value: number }).value || 10) + 12
165
- )
159
+ .attr('y', (d) => Math.sqrt(d.value || 10) + 12)
166
160
  .attr('text-anchor', 'middle')
167
161
  .attr('fill', effectiveTheme === 'dark' ? '#e2e8f0' : '#1e293b')
168
162
  .attr('font-size', '9px')
@@ -170,47 +164,42 @@ export function GraphCanvas({
170
164
  .attr('pointer-events', 'none');
171
165
 
172
166
  // Add tooltips
173
- node.append('title').text((d: unknown) => (d as { title: string }).title);
167
+ node.append('title').text((d) => d.title || '');
174
168
 
175
169
  // Event handlers
176
- node.on('click', (event: unknown, d: unknown) => {
177
- (event as Event).stopPropagation();
178
- onNodeClick(d as FileNode);
170
+ node.on('click', (event: MouseEvent, d: D3Node) => {
171
+ event.stopPropagation();
172
+ onNodeClick(d);
179
173
  });
180
174
  svg.on('click', () => onNodeClick(null));
181
175
 
182
176
  // Simulation tick
183
177
  simulation.on('tick', () => {
184
178
  link
185
- .attr('x1', (d: unknown) => (d as { source: { x: number } }).source.x)
186
- .attr('y1', (d: unknown) => (d as { source: { y: number } }).source.y)
187
- .attr('x2', (d: unknown) => (d as { target: { x: number } }).target.x)
188
- .attr('y2', (d: unknown) => (d as { target: { y: number } }).target.y);
179
+ .attr('x1', (d) => (d.source as D3Node).x || 0)
180
+ .attr('y1', (d) => (d.source as D3Node).y || 0)
181
+ .attr('x2', (d) => (d.target as D3Node).x || 0)
182
+ .attr('y2', (d) => (d.target as D3Node).y || 0);
189
183
 
190
- node.attr(
191
- 'transform',
192
- (d: unknown) =>
193
- `translate(${(d as { x: number }).x},${(d as { y: number }).y})`
194
- );
184
+ node.attr('transform', (d) => `translate(${d.x || 0},${d.y || 0})`);
195
185
  });
196
186
 
197
187
  // Drag functions
198
- function dragstarted(event: unknown, d: unknown) {
199
- if (!(event as { active: boolean }).active)
200
- simulation.alphaTarget(0.3).restart();
201
- (d as { fx: number | null }).fx = (d as { x: number }).x;
202
- (d as { fy: number | null }).fy = (d as { y: number }).y;
188
+ function dragstarted(event: d3.D3DragEvent<SVGGElement, D3Node, D3Node>, d: D3Node) {
189
+ if (!event.active) simulation.alphaTarget(0.3).restart();
190
+ d.fx = d.x;
191
+ d.fy = d.y;
203
192
  }
204
193
 
205
- function dragged(event: unknown, d: unknown) {
206
- (d as { fx: number }).fx = (event as { x: number }).x;
207
- (d as { fy: number }).fy = (event as { y: number }).y;
194
+ function dragged(event: d3.D3DragEvent<SVGGElement, D3Node, D3Node>, d: D3Node) {
195
+ d.fx = event.x;
196
+ d.fy = event.y;
208
197
  }
209
198
 
210
- function dragended(event: unknown, d: unknown) {
211
- if (!(event as { active: boolean }).active) simulation.alphaTarget(0);
212
- (d as { fx: null }).fx = null;
213
- (d as { fy: null }).fy = null;
199
+ function dragended(event: d3.D3DragEvent<SVGGElement, D3Node, D3Node>, d: D3Node) {
200
+ if (!event.active) simulation.alphaTarget(0);
201
+ d.fx = null;
202
+ d.fy = null;
214
203
  }
215
204
 
216
205
  return () => {