@mauroandre/velojs 0.0.41 → 0.0.43
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.
- package/dist/graph-viewer.js +84 -85
- package/package.json +1 -1
package/dist/graph-viewer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import r from "node:http";
|
|
2
2
|
import d from "node:fs";
|
|
3
|
-
import
|
|
4
|
-
const
|
|
3
|
+
import l from "node:path";
|
|
4
|
+
const s = `<!DOCTYPE html>
|
|
5
5
|
<html lang="en">
|
|
6
6
|
<head>
|
|
7
7
|
<meta charset="utf-8">
|
|
@@ -48,7 +48,7 @@ const l = `<!DOCTYPE html>
|
|
|
48
48
|
<div><i style="background:#5b8def"></i>page</div>
|
|
49
49
|
<div><i style="background:#4b5565"></i>module</div>
|
|
50
50
|
</div>
|
|
51
|
-
<div id="hint">drag to pan · scroll to zoom · drag nodes · hover =
|
|
51
|
+
<div id="hint">drag to pan · scroll to zoom · drag nodes · hover = highlight · click = pin highlight + details · / = search · esc = reset</div>
|
|
52
52
|
<script>
|
|
53
53
|
var KIND_COLORS = { root: "#f59e0b", layout: "#a78bfa", page: "#5b8def", unknown: "#4b5565" };
|
|
54
54
|
|
|
@@ -60,7 +60,8 @@ var ctx = canvas.getContext("2d");
|
|
|
60
60
|
var W = 0, H = 0;
|
|
61
61
|
|
|
62
62
|
function resize() {
|
|
63
|
-
|
|
63
|
+
var tb = document.getElementById("topbar");
|
|
64
|
+
W = window.innerWidth; H = window.innerHeight - tb.offsetHeight;
|
|
64
65
|
canvas.width = W * devicePixelRatio; canvas.height = H * devicePixelRatio;
|
|
65
66
|
canvas.style.width = W + "px"; canvas.style.height = H + "px";
|
|
66
67
|
ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0);
|
|
@@ -152,47 +153,34 @@ function tick() {
|
|
|
152
153
|
});
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
// ----------
|
|
156
|
-
//
|
|
157
|
-
// (
|
|
158
|
-
var
|
|
159
|
-
function
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
var
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
function
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
function
|
|
178
|
-
function routeVisible(n) { return matches(n.data.moduleId || "(wrapper)") && (!focusRouteSet || focusRouteSet.indexOf(n) >= 0); }
|
|
179
|
-
|
|
180
|
-
// ---------- impact highlight ----------
|
|
181
|
-
var hoverNode = null;
|
|
182
|
-
var impactSet = null;
|
|
183
|
-
function computeImpact(n) {
|
|
184
|
-
var set = {}, queue = [n.id];
|
|
185
|
-
set[n.id] = 2;
|
|
186
|
-
while (queue.length) {
|
|
187
|
-
var id = queue.shift();
|
|
188
|
-
var mod = graph.modules[id];
|
|
189
|
-
if (!mod) continue;
|
|
190
|
-
mod.importedBy.forEach(function (up) {
|
|
191
|
-
if (!(up in set)) { set[up] = 1; queue.push(up); }
|
|
192
|
-
});
|
|
156
|
+
// ---------- highlight — hover = transient, click = pinned ----------
|
|
157
|
+
// Same pattern in both views. Related set: modules → transitive
|
|
158
|
+
// importedBy (impact); routes → ancestors + subtree (context).
|
|
159
|
+
var hoverHL = null, pinnedHL = null;
|
|
160
|
+
function activeHL() { return pinnedHL || hoverHL; }
|
|
161
|
+
function relatedNodes(node) {
|
|
162
|
+
var list = [];
|
|
163
|
+
if (view === "modules") {
|
|
164
|
+
var seen = {}, q = [node];
|
|
165
|
+
seen[node.id] = true;
|
|
166
|
+
while (q.length) {
|
|
167
|
+
var cur = q.shift();
|
|
168
|
+
list.push(cur);
|
|
169
|
+
var m = graph.modules[cur.id];
|
|
170
|
+
if (!m) continue;
|
|
171
|
+
m.importedBy.forEach(function (i) {
|
|
172
|
+
if (modById[i] && !seen[i]) { seen[i] = true; q.push(modById[i]); }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
var p = node.parent;
|
|
177
|
+
while (p) { list.push(p); p = p.parent; }
|
|
178
|
+
(function down(n) { list.push(n); n.kids.forEach(down); })(node);
|
|
193
179
|
}
|
|
194
|
-
return
|
|
180
|
+
return list;
|
|
195
181
|
}
|
|
182
|
+
function modVisible(n) { return matches(n.id); }
|
|
183
|
+
function routeVisible(n) { return matches(n.data.moduleId || "(wrapper)"); }
|
|
196
184
|
|
|
197
185
|
// ---------- selection / panel ----------
|
|
198
186
|
var selected = null;
|
|
@@ -233,51 +221,53 @@ var query = "";
|
|
|
233
221
|
document.getElementById("search").addEventListener("input", function (e) { query = e.target.value.toLowerCase(); });
|
|
234
222
|
document.addEventListener("keydown", function (e) {
|
|
235
223
|
if (e.key === "/" && document.activeElement !== document.getElementById("search")) { e.preventDefault(); document.getElementById("search").focus(); }
|
|
236
|
-
if (e.key === "Escape") { panel.className = ""; selected = null; query = ""; document.getElementById("search").value = "";
|
|
224
|
+
if (e.key === "Escape") { panel.className = ""; selected = null; query = ""; document.getElementById("search").value = ""; hoverHL = null; pinnedHL = null; }
|
|
237
225
|
});
|
|
238
226
|
|
|
239
227
|
// ---------- interaction ----------
|
|
240
228
|
var dragNode = null, panning = false, lastX = 0, lastY = 0, moved = false;
|
|
241
229
|
function nodeAt(sx, sy) {
|
|
242
|
-
|
|
230
|
+
// Hit test in SCREEN space, tight to the dot (dot radius + 6px slack)
|
|
243
231
|
var list = view === "modules" ? modNodes : routeNodes;
|
|
244
232
|
for (var i = list.length - 1; i >= 0; i--) {
|
|
245
233
|
var n = list[i];
|
|
246
234
|
if (view === "modules" ? !modVisible(n) : !routeVisible(n)) continue;
|
|
247
|
-
var
|
|
248
|
-
var
|
|
249
|
-
|
|
235
|
+
var wx = view === "modules" ? n.x : n.px, wy = view === "modules" ? n.y : n.py;
|
|
236
|
+
var p = toScreen(wx, wy);
|
|
237
|
+
var rad = (view === "modules" ? (n.kind === "unknown" ? 5 : 8) : 6) + 6;
|
|
238
|
+
var dx = sx - p[0], dy = sy - p[1];
|
|
239
|
+
if (dx * dx + dy * dy < rad * rad) return n;
|
|
250
240
|
}
|
|
251
241
|
return null;
|
|
252
242
|
}
|
|
253
243
|
canvas.addEventListener("mousedown", function (e) {
|
|
254
|
-
var n = nodeAt(e.
|
|
244
|
+
var n = nodeAt(e.offsetX, e.offsetY);
|
|
255
245
|
if (n && view === "modules") { dragNode = n; alpha = Math.max(alpha, 0.3); }
|
|
256
246
|
else panning = true;
|
|
257
|
-
lastX = e.
|
|
247
|
+
lastX = e.offsetX; lastY = e.offsetY; moved = false;
|
|
258
248
|
});
|
|
259
249
|
canvas.addEventListener("mousemove", function (e) {
|
|
260
|
-
var dx = e.
|
|
250
|
+
var dx = e.offsetX - lastX, dy = e.offsetY - lastY;
|
|
261
251
|
if (Math.abs(dx) + Math.abs(dy) > 3) moved = true;
|
|
262
|
-
if (dragNode) { var w = toWorld(e.
|
|
252
|
+
if (dragNode) { var w = toWorld(e.offsetX, e.offsetY); dragNode.x = w[0]; dragNode.y = w[1]; dragNode.vx = dragNode.vy = 0; }
|
|
263
253
|
else if (panning) { cam.x += dx; cam.y += dy; }
|
|
264
254
|
else {
|
|
265
|
-
var n = nodeAt(e.
|
|
266
|
-
|
|
267
|
-
impactSet = hoverNode ? computeImpact(hoverNode) : null;
|
|
255
|
+
var n = nodeAt(e.offsetX, e.offsetY);
|
|
256
|
+
hoverHL = n ? { list: relatedNodes(n), origin: n } : null;
|
|
268
257
|
canvas.style.cursor = n ? "pointer" : "grab";
|
|
269
258
|
}
|
|
270
|
-
lastX = e.
|
|
259
|
+
lastX = e.offsetX; lastY = e.offsetY;
|
|
271
260
|
});
|
|
272
261
|
canvas.addEventListener("mouseup", function (e) {
|
|
273
262
|
if (!moved) {
|
|
274
|
-
var n = nodeAt(e.
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
|
|
263
|
+
var n = nodeAt(e.offsetX, e.offsetY);
|
|
264
|
+
if (n) {
|
|
265
|
+
pinnedHL = { list: relatedNodes(n), origin: n };
|
|
266
|
+
var id = view === "modules" ? n.id : n.data.moduleId;
|
|
267
|
+
showPanel(id || null);
|
|
278
268
|
} else {
|
|
279
|
-
|
|
280
|
-
|
|
269
|
+
pinnedHL = null;
|
|
270
|
+
showPanel(null);
|
|
281
271
|
}
|
|
282
272
|
}
|
|
283
273
|
dragNode = null; panning = false;
|
|
@@ -285,9 +275,9 @@ canvas.addEventListener("mouseup", function (e) {
|
|
|
285
275
|
canvas.addEventListener("wheel", function (e) {
|
|
286
276
|
e.preventDefault();
|
|
287
277
|
var k = Math.exp(-e.deltaY * 0.0012);
|
|
288
|
-
var w = toWorld(e.
|
|
278
|
+
var w = toWorld(e.offsetX, e.offsetY);
|
|
289
279
|
cam.k *= k;
|
|
290
|
-
cam.x = e.
|
|
280
|
+
cam.x = e.offsetX - w[0] * cam.k; cam.y = e.offsetY - w[1] * cam.k;
|
|
291
281
|
}, { passive: false });
|
|
292
282
|
|
|
293
283
|
// ---------- rendering ----------
|
|
@@ -295,64 +285,73 @@ function matches(id) { return !query || id.toLowerCase().indexOf(query) >= 0; }
|
|
|
295
285
|
|
|
296
286
|
function drawRoutes() {
|
|
297
287
|
ctx.clearRect(0, 0, W, H);
|
|
298
|
-
|
|
288
|
+
var hl = activeHL();
|
|
299
289
|
routeEdges.forEach(function (e) {
|
|
300
290
|
if (!routeVisible(e.from) || !routeVisible(e.to)) return;
|
|
291
|
+
var hot = hl && hl.list.indexOf(e.from) >= 0 && hl.list.indexOf(e.to) >= 0;
|
|
301
292
|
var a = toScreen(e.from.px, e.from.py), b = toScreen(e.to.px, e.to.py);
|
|
293
|
+
ctx.globalAlpha = hl && !hot ? 0.08 : 1;
|
|
294
|
+
ctx.strokeStyle = hot ? "#f59e0b" : "#2a3247";
|
|
295
|
+
ctx.lineWidth = hot ? 2 : 1.2;
|
|
302
296
|
ctx.beginPath();
|
|
303
297
|
ctx.moveTo(a[0], a[1]);
|
|
304
298
|
ctx.bezierCurveTo(a[0], (a[1] + b[1]) / 2, b[0], (a[1] + b[1]) / 2, b[0], b[1]);
|
|
305
299
|
ctx.stroke();
|
|
306
300
|
});
|
|
301
|
+
ctx.globalAlpha = 1;
|
|
307
302
|
routeNodes.forEach(function (n) {
|
|
308
303
|
var d = n.data;
|
|
309
304
|
var id = d.moduleId || "(wrapper)";
|
|
310
305
|
if (!routeVisible(n)) return;
|
|
311
306
|
var p = toScreen(n.px, n.py);
|
|
307
|
+
var member = hl && hl.list.indexOf(n) >= 0;
|
|
308
|
+
var dim = hl && !member;
|
|
309
|
+
var isOrigin = hl && hl.origin === n;
|
|
312
310
|
var color = d.isRoot ? KIND_COLORS.root : (d.children.length ? KIND_COLORS.layout : KIND_COLORS.page);
|
|
313
311
|
var sel = selected === d.moduleId;
|
|
314
|
-
ctx.
|
|
315
|
-
ctx.
|
|
312
|
+
ctx.globalAlpha = dim ? 0.15 : 1;
|
|
313
|
+
ctx.beginPath(); ctx.arc(p[0], p[1], (sel || isOrigin) ? 9 : 6, 0, 7);
|
|
314
|
+
ctx.fillStyle = isOrigin ? "#f59e0b" : color; ctx.fill();
|
|
316
315
|
if (sel) { ctx.strokeStyle = "#e8edf5"; ctx.lineWidth = 2; ctx.stroke(); }
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (cam.k >= 0.55 || sel) {
|
|
320
|
-
ctx.fillStyle = "#d7dde8"; ctx.font = "11px ui-monospace, monospace"; ctx.textAlign = "center";
|
|
316
|
+
if (!dim) {
|
|
317
|
+
ctx.fillStyle = member ? "#e8edf5" : "#d7dde8"; ctx.font = "11px ui-monospace, monospace"; ctx.textAlign = "center";
|
|
321
318
|
ctx.fillText(id.split("/").pop(), p[0], p[1] - 13);
|
|
322
|
-
}
|
|
323
|
-
if (cam.k >= 0.85 || sel) {
|
|
324
319
|
ctx.fillStyle = "#55607a"; ctx.font = "10px ui-monospace, monospace";
|
|
325
320
|
var sub = d.fullPath || "";
|
|
326
321
|
if (d.middlewares.length) sub += " · " + d.middlewares.join(", ");
|
|
327
322
|
ctx.fillText(sub, p[0], p[1] + 20);
|
|
328
323
|
}
|
|
324
|
+
ctx.globalAlpha = 1;
|
|
329
325
|
});
|
|
330
326
|
}
|
|
331
327
|
|
|
332
328
|
function drawModules() {
|
|
333
329
|
ctx.clearRect(0, 0, W, H);
|
|
334
|
-
|
|
330
|
+
var hl = activeHL();
|
|
335
331
|
modEdges.forEach(function (e) {
|
|
336
332
|
if (!modVisible(e.from) || !modVisible(e.to)) return;
|
|
337
|
-
var hot =
|
|
333
|
+
var hot = hl && hl.list.indexOf(e.from) >= 0 && hl.list.indexOf(e.to) >= 0;
|
|
338
334
|
var a = toScreen(e.from.x, e.from.y), b = toScreen(e.to.x, e.to.y);
|
|
335
|
+
ctx.globalAlpha = hl && !hot ? 0.08 : 1;
|
|
339
336
|
ctx.strokeStyle = hot ? "#f59e0b" : "#232a3a";
|
|
340
337
|
ctx.lineWidth = hot ? 1.8 : 1;
|
|
341
338
|
ctx.beginPath(); ctx.moveTo(a[0], a[1]); ctx.lineTo(b[0], b[1]); ctx.stroke();
|
|
342
339
|
});
|
|
340
|
+
ctx.globalAlpha = 1;
|
|
343
341
|
modNodes.forEach(function (n) {
|
|
344
342
|
if (!modVisible(n)) return;
|
|
345
343
|
var p = toScreen(n.x, n.y);
|
|
346
|
-
var
|
|
347
|
-
var
|
|
348
|
-
var
|
|
344
|
+
var member = hl && hl.list.indexOf(n) >= 0;
|
|
345
|
+
var dim = hl && !member;
|
|
346
|
+
var isOrigin = hl && hl.origin === n;
|
|
347
|
+
var r = n.kind === "unknown" ? 5 : 8;
|
|
349
348
|
ctx.globalAlpha = dim ? 0.15 : 1;
|
|
350
|
-
ctx.beginPath(); ctx.arc(p[0], p[1], selected === n.id ? r + 3 : r, 0, 7);
|
|
351
|
-
ctx.fillStyle =
|
|
349
|
+
ctx.beginPath(); ctx.arc(p[0], p[1], (selected === n.id || isOrigin) ? r + 3 : r, 0, 7);
|
|
350
|
+
ctx.fillStyle = isOrigin ? "#f59e0b" : KIND_COLORS[n.kind] || KIND_COLORS.unknown;
|
|
352
351
|
ctx.fill();
|
|
353
352
|
if (selected === n.id) { ctx.strokeStyle = "#e8edf5"; ctx.lineWidth = 2; ctx.stroke(); }
|
|
354
353
|
if (!dim) {
|
|
355
|
-
ctx.fillStyle =
|
|
354
|
+
ctx.fillStyle = member ? "#e8edf5" : "#9aa5b8";
|
|
356
355
|
ctx.font = "10px ui-monospace, monospace"; ctx.textAlign = "center";
|
|
357
356
|
ctx.fillText(n.id.split("/").pop(), p[0], p[1] - r - 5);
|
|
358
357
|
}
|
|
@@ -391,8 +390,8 @@ fetch("/graph.json").then(function (r) { return r.json(); }).then(function (g) {
|
|
|
391
390
|
<\/script>
|
|
392
391
|
</body>
|
|
393
392
|
</html>`;
|
|
394
|
-
async function
|
|
395
|
-
const i =
|
|
393
|
+
async function m(a) {
|
|
394
|
+
const i = l.join(a, ".velojs", "graph.json"), o = r.createServer((n, e) => {
|
|
396
395
|
if (n.url === "/graph.json") {
|
|
397
396
|
let t;
|
|
398
397
|
try {
|
|
@@ -404,7 +403,7 @@ async function f(a) {
|
|
|
404
403
|
e.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" }), e.end(t);
|
|
405
404
|
return;
|
|
406
405
|
}
|
|
407
|
-
e.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), e.end(
|
|
406
|
+
e.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), e.end(s);
|
|
408
407
|
});
|
|
409
408
|
return await new Promise((n) => {
|
|
410
409
|
o.listen(0, "127.0.0.1", () => {
|
|
@@ -414,5 +413,5 @@ async function f(a) {
|
|
|
414
413
|
});
|
|
415
414
|
}
|
|
416
415
|
export {
|
|
417
|
-
|
|
416
|
+
m as serveGraphViewer
|
|
418
417
|
};
|