Politur: Gedaechnis-Graph-Visualisierung veredelt (Neon-Glow, radialer Hintergrundverlauf und reaktives Zoom-Scaling)

This commit is contained in:
Hitonabi
2026-07-09 12:14:32 +02:00
parent c20c070a6b
commit 3ea0e2d540
6 changed files with 42 additions and 27 deletions
+29 -14
View File
@@ -9,16 +9,16 @@ import { type MemoryGraph } from "@/lib/api"
// Fakten flüssig. Im App-Look gestylt; Hover hebt den Knoten + seine Nachbarn hervor, der Rest dimmt.
const CAT_COLOR: Record<string, string> = {
identity: "#22d3ee", knowledge: "#6366f1", rules: "#a78bfa", events: "#fbbf24",
identity: "#00f5ff", knowledge: "#3b82f6", rules: "#d946ef", events: "#fbbf24",
}
const CAT_LABEL: Record<string, string> = {
identity: "Identität", knowledge: "Wissen", rules: "Regeln", events: "Ereignisse",
}
const AUTO = new Set(["auto", "agent", "hermes"])
const DIM_NODE = "#1f2937"
const DIM_EDGE = "#0f172a"
const EDGE = "#243044"
const EDGE_HI = "#4b5563"
const DIM_NODE = "#1e293b"
const DIM_EDGE = "rgba(15, 23, 42, 0.05)"
const EDGE = "rgba(255, 255, 255, 0.07)"
const EDGE_HI = "rgba(99, 102, 241, 0.85)"
export function GraphView({ data, onDelete }: { data: MemoryGraph; onDelete: (id: string) => void }) {
const [selected, setSelected] = useState<string | null>(null)
@@ -43,39 +43,53 @@ export function GraphView({ data, onDelete }: { data: MemoryGraph; onDelete: (id
const a = (2 * Math.PI * i) / N
graph.addNode(n.id, {
x: Math.cos(a), y: Math.sin(a), // Startposition (Kreis) für ForceAtlas2
size: 4 + Math.min(degree[n.id] || 0, 12) * 1.4,
size: 5 + Math.min(degree[n.id] || 0, 12) * 1.5,
color: CAT_COLOR[n.category] || "#64748b",
label: n.content.length > 48 ? n.content.slice(0, 47) + "…" : n.content,
})
})
data.edges.forEach((e) => {
if (graph.hasNode(e.source) && graph.hasNode(e.target) && !graph.hasEdge(e.source, e.target))
graph.addEdge(e.source, e.target, { size: 0.6 + (e.weight || 0), color: EDGE })
graph.addEdge(e.source, e.target, { size: 0.8 + (e.weight || 0) * 0.5, color: EDGE })
})
forceAtlas2.assign(graph, { iterations: 220, settings: forceAtlas2.inferSettings(graph) })
graphRef.current = graph
const renderer = new Sigma(graph, containerRef.current, {
renderLabels: true,
labelColor: { color: "#cbd5e1" },
labelColor: { color: "#e2e8f0" },
labelSize: 11,
labelWeight: "500",
labelDensity: 0.6,
labelWeight: "600",
labelDensity: 0.5,
labelRenderedSizeThreshold: N > 120 ? 12 : 0, // bei vielen Knoten nur große labeln (entklumpen)
defaultEdgeColor: EDGE,
minCameraRatio: 0.1,
maxCameraRatio: 4,
nodeReducer: (id, attrs) => {
const a = active.current
if (!a) return attrs
if (id === a || graph.areNeighbors(a, id)) return attrs
const isSel = id === selectedRef.current
if (!a) {
if (isSel) return { ...attrs, size: attrs.size * 1.35, highlighted: true }
return attrs
}
if (id === a) {
return { ...attrs, size: attrs.size * 1.45, highlighted: true }
}
if (graph.areNeighbors(a, id)) {
if (isSel) return { ...attrs, size: attrs.size * 1.35, highlighted: true }
return attrs
}
return { ...attrs, color: DIM_NODE, label: "" }
},
edgeReducer: (edge, attrs) => {
const a = active.current
if (!a) return attrs
const ext = graph.extremities(edge)
if (ext[0] === a || ext[1] === a) return { ...attrs, color: EDGE_HI, size: (attrs.size || 1) * 1.6 }
if (ext[0] === a || ext[1] === a) return { ...attrs, color: EDGE_HI, size: (attrs.size || 1) * 2.2 }
return { ...attrs, color: DIM_EDGE }
},
})
@@ -147,7 +161,8 @@ export function GraphView({ data, onDelete }: { data: MemoryGraph; onDelete: (id
return (
<div className="grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-3">
<div className="relative h-[calc(100vh-13rem)] min-h-[560px] rounded-2xl border border-border/60 bg-[#070a0f] overflow-hidden">
<div className="relative h-[calc(100vh-13rem)] min-h-[560px] rounded-2xl border border-border/60 overflow-hidden"
style={{ background: "radial-gradient(circle at center, rgba(59, 130, 246, 0.06) 0%, #030712 100%)" }}>
<div ref={containerRef} className="absolute inset-0" />
<button onClick={relayout} title="Knoten neu anordnen"
className="absolute right-3 top-3 z-10 flex items-center gap-1.5 rounded-lg border border-border/60 bg-background/50 px-2.5 py-1.5 text-[11px] text-muted-foreground hover:text-foreground backdrop-blur-sm transition-colors">