Politur: Robustes ID-Parsing bei D3-Objektmutationen fuer hover/degree in GraphView behoben

This commit is contained in:
Hitonabi
2026-07-09 12:39:25 +02:00
parent 103b277643
commit 082f4a659d
4 changed files with 24 additions and 20 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -7,7 +7,7 @@
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-C6kUDd9F.js"></script>
<script type="module" crossorigin src="/assets/index-Bz6q5kVA.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index--2fz5jZz.css">
</head>
<body>
+15 -11
View File
@@ -61,11 +61,16 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
}
}, [data])
// Grad (Degree) berechnen fängt D3-Objektmutationen robust ab!
const degree = useMemo(() => {
const d: Record<string, number> = {}
data.edges.forEach((e) => {
d[e.source] = (d[e.source] || 0) + 1
d[e.target] = (d[e.target] || 0) + 1
const sId = typeof e.source === "object" ? (e.source as any).id : e.source
const tId = typeof e.target === "object" ? (e.target as any).id : e.target
if (sId && tId) {
d[sId] = (d[sId] || 0) + 1
d[tId] = (d[tId] || 0) + 1
}
})
return d
}, [data])
@@ -98,24 +103,20 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
fg.d3Force("center", null)
// 2. Clustering-Kräfte (Knoten zu ihren jeweiligen Sektions-Foci ziehen)
// Reduzierte Stärke (0.07 statt 0.18), damit sie sich nicht zu einem dichten Brei zusammenballen.
fg.d3Force("x", forceX().x((node: any) => getClusterX(node.category)).strength(0.07))
fg.d3Force("y", forceY().y((node: any) => getClusterY(node.category)).strength(0.07))
// 3. Starke Abstoßung (Charge) für ein weit gefächertes, luftiges Netz
// Erhöht auf -380, damit sich die Knoten kräftiger abstoßen.
const charge = fg.d3Force("charge")
if (charge) charge.strength(-380)
// 4. Kollisionskraft (Sicherer Radius, verhindert Überlappung)
// Wenn der Knoten hovered oder selected ist, geben wir ihm mehr Platz
fg.d3Force("collide", forceCollide().radius((node: any) => {
const isLarge = node.id === hovered || node.id === selectedNodeId
return isLarge ? 50 : 25
}).iterations(2))
// 5. Federkraft (Links)
// Längere Verbindungsstriche (85 statt 60), damit das Netz übersichtlicher und "verbindungsreicher" wirkt.
const link = fg.d3Force("link")
if (link) link.distance(85)
@@ -131,13 +132,15 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
fgRef.current.zoomToFit(400, 40)
}
// Nachbarmenge für extrem schnellen Lookup im Render-Loop
// Nachbarmenge für extrem schnellen Lookup fängt D3-Objektmutationen robust ab!
const connectedNodes = useMemo(() => {
if (!activeNode) return new Set<string>()
const set = new Set<string>()
data.edges.forEach((e) => {
if (e.source === activeNode) set.add(e.target)
if (e.target === activeNode) set.add(e.source)
const sId = typeof e.source === "object" ? (e.source as any).id : e.source
const tId = typeof e.target === "object" ? (e.target as any).id : e.target
if (sId === activeNode) set.add(tId)
if (tId === activeNode) set.add(sId)
})
return set
}, [activeNode, data])
@@ -154,11 +157,12 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
backgroundColor="rgba(0,0,0,0)"
cooldownTicks={120}
// Exakte Hit-Map auf unsichtbarem Canvas zeichnen für 100% zuverlässiges Hovern und Klicken
// Exakte Hit-Map auf unsichtbarem Canvas zeichnen fängt D3-Objektmutationen robust ab!
nodePointerAreaPaint={(node: any, color: string, ctx: CanvasRenderingContext2D) => {
const x = node.x ?? 0
const y = node.y ?? 0
const size = 6 + Math.min(degree[node.id] || 0, 15) * 0.8
const nId = typeof node === "object" ? node.id : node
const size = 6 + Math.min(degree[nId] || 0, 15) * 0.8
ctx.fillStyle = color
ctx.beginPath()
ctx.arc(x, y, size + 5, 0, 2 * Math.PI, false) // 5px Puffer für einfaches Treffen