html-overlay-node 0.1.0

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.
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Minimap - Shows overview of entire graph with viewport indicator
3
+ */
4
+ export class Minimap {
5
+ constructor(container, { graph, renderer, width = 200, height = 150 } = {}) {
6
+ this.graph = graph;
7
+ this.renderer = renderer;
8
+ this.width = width;
9
+ this.height = height;
10
+
11
+ // Create canvas element
12
+ this.canvas = document.createElement("canvas");
13
+ this.canvas.id = "minimap";
14
+ this.canvas.width = width;
15
+ this.canvas.height = height;
16
+ this.canvas.style.position = "fixed";
17
+ this.canvas.style.bottom = "20px";
18
+ this.canvas.style.right = "20px";
19
+ this.canvas.style.border = "2px solid #444";
20
+ this.canvas.style.borderRadius = "8px";
21
+ this.canvas.style.background = "rgba(20, 20, 23, 0.9)";
22
+ this.canvas.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.5)";
23
+ this.canvas.style.pointerEvents = "none"; // Don't block clicks
24
+
25
+ this.ctx = this.canvas.getContext("2d");
26
+
27
+ // Add to container
28
+ container.appendChild(this.canvas);
29
+ }
30
+
31
+ /**
32
+ * Render the minimap
33
+ */
34
+ render() {
35
+ const { graph, renderer, ctx, width: w, height: h } = this;
36
+
37
+ // Clear
38
+ ctx.fillStyle = "#141417";
39
+ ctx.fillRect(0, 0, w, h);
40
+
41
+ if (graph.nodes.size === 0) return;
42
+
43
+ // Calculate bounds of all nodes
44
+ let minX = Infinity,
45
+ minY = Infinity,
46
+ maxX = -Infinity,
47
+ maxY = -Infinity;
48
+ for (const node of graph.nodes.values()) {
49
+ const { x, y, w: nw, h: nh } = node.computed;
50
+ minX = Math.min(minX, x);
51
+ minY = Math.min(minY, y);
52
+ maxX = Math.max(maxX, x + nw);
53
+ maxY = Math.max(maxY, y + nh);
54
+ }
55
+
56
+ // Add margin to the bounds
57
+ const margin = 100; // World units margin
58
+ const graphWidth = Math.max(300, maxX - minX + margin * 2);
59
+ const graphHeight = Math.max(200, maxY - minY + margin * 2);
60
+
61
+ // Adjust minX and minY to center the content
62
+ minX -= margin;
63
+ minY -= margin;
64
+
65
+ const padding = 10;
66
+
67
+ const scale = Math.min(
68
+ (w - padding * 2) / graphWidth,
69
+ (h - padding * 2) / graphHeight
70
+ );
71
+
72
+ const offsetX = (w - graphWidth * scale) / 2;
73
+ const offsetY = (h - graphHeight * scale) / 2;
74
+
75
+ // Draw edges first (so they appear behind nodes)
76
+ ctx.strokeStyle = "rgba(127, 140, 255, 0.5)"; // Semi-transparent edge color
77
+ ctx.lineWidth = 1;
78
+ for (const edge of graph.edges.values()) {
79
+ const fromNode = graph.nodes.get(edge.fromNode);
80
+ const toNode = graph.nodes.get(edge.toNode);
81
+ if (!fromNode || !toNode) continue;
82
+
83
+ // Get center points of nodes
84
+ const x1 = fromNode.computed.x + fromNode.computed.w / 2;
85
+ const y1 = fromNode.computed.y + fromNode.computed.h / 2;
86
+ const x2 = toNode.computed.x + toNode.computed.w / 2;
87
+ const y2 = toNode.computed.y + toNode.computed.h / 2;
88
+
89
+ // Transform to minimap coordinates
90
+ const mx1 = (x1 - minX) * scale + offsetX;
91
+ const my1 = (y1 - minY) * scale + offsetY;
92
+ const mx2 = (x2 - minX) * scale + offsetX;
93
+ const my2 = (y2 - minY) * scale + offsetY;
94
+
95
+ ctx.beginPath();
96
+ ctx.moveTo(mx1, my1);
97
+ ctx.lineTo(mx2, my2);
98
+ ctx.stroke();
99
+ }
100
+
101
+ // Draw nodes
102
+ ctx.fillStyle = "#6cf";
103
+ for (const node of graph.nodes.values()) {
104
+ const { x, y, w: nw, h: nh } = node.computed;
105
+ const mx = (x - minX) * scale + offsetX;
106
+ const my = (y - minY) * scale + offsetY;
107
+ const mw = nw * scale;
108
+ const mh = nh * scale;
109
+
110
+ if (node.type === "core/Group") {
111
+ ctx.fillStyle = "rgba(102, 204, 255, 0.2)";
112
+ ctx.strokeStyle = "#6cf";
113
+ ctx.lineWidth = 1;
114
+ ctx.fillRect(mx, my, mw, mh);
115
+ ctx.strokeRect(mx, my, mw, mh);
116
+ } else {
117
+ ctx.fillStyle = "#6cf";
118
+ ctx.fillRect(mx, my, Math.max(2, mw), Math.max(2, mh));
119
+ }
120
+ }
121
+
122
+ // Draw viewport rectangle
123
+ const vx0 = -renderer.offsetX / renderer.scale;
124
+ const vy0 = -renderer.offsetY / renderer.scale;
125
+ const vw = renderer.canvas.width / renderer.scale;
126
+ const vh = renderer.canvas.height / renderer.scale;
127
+
128
+ const vmx = (vx0 - minX) * scale + offsetX;
129
+ const vmy = (vy0 - minY) * scale + offsetY;
130
+ const vmw = vw * scale;
131
+ const vmh = vh * scale;
132
+
133
+ ctx.strokeStyle = "#ff6b6b";
134
+ ctx.lineWidth = 2;
135
+ ctx.strokeRect(vmx, vmy, vmw, vmh);
136
+ }
137
+
138
+ /**
139
+ * Cleanup
140
+ */
141
+ destroy() {
142
+ if (this.canvas.parentElement) {
143
+ this.canvas.parentElement.removeChild(this.canvas);
144
+ }
145
+ }
146
+ }