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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -7,8 +7,8 @@
<link rel="manifest" href="/manifest.webmanifest" /> <link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Mission Control 2.0</title> <title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-Cp5Tizb9.js"></script> <script type="module" crossorigin src="/assets/index-r382vnGM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DMTEFdKw.css"> <link rel="stylesheet" crossorigin href="/assets/index-B1ESaWd9.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+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. // Fakten flüssig. Im App-Look gestylt; Hover hebt den Knoten + seine Nachbarn hervor, der Rest dimmt.
const CAT_COLOR: Record<string, string> = { 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> = { const CAT_LABEL: Record<string, string> = {
identity: "Identität", knowledge: "Wissen", rules: "Regeln", events: "Ereignisse", identity: "Identität", knowledge: "Wissen", rules: "Regeln", events: "Ereignisse",
} }
const AUTO = new Set(["auto", "agent", "hermes"]) const AUTO = new Set(["auto", "agent", "hermes"])
const DIM_NODE = "#1f2937" const DIM_NODE = "#1e293b"
const DIM_EDGE = "#0f172a" const DIM_EDGE = "rgba(15, 23, 42, 0.05)"
const EDGE = "#243044" const EDGE = "rgba(255, 255, 255, 0.07)"
const EDGE_HI = "#4b5563" const EDGE_HI = "rgba(99, 102, 241, 0.85)"
export function GraphView({ data, onDelete }: { data: MemoryGraph; onDelete: (id: string) => void }) { export function GraphView({ data, onDelete }: { data: MemoryGraph; onDelete: (id: string) => void }) {
const [selected, setSelected] = useState<string | null>(null) 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 const a = (2 * Math.PI * i) / N
graph.addNode(n.id, { graph.addNode(n.id, {
x: Math.cos(a), y: Math.sin(a), // Startposition (Kreis) für ForceAtlas2 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", color: CAT_COLOR[n.category] || "#64748b",
label: n.content.length > 48 ? n.content.slice(0, 47) + "…" : n.content, label: n.content.length > 48 ? n.content.slice(0, 47) + "…" : n.content,
}) })
}) })
data.edges.forEach((e) => { data.edges.forEach((e) => {
if (graph.hasNode(e.source) && graph.hasNode(e.target) && !graph.hasEdge(e.source, e.target)) 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) }) forceAtlas2.assign(graph, { iterations: 220, settings: forceAtlas2.inferSettings(graph) })
graphRef.current = graph graphRef.current = graph
const renderer = new Sigma(graph, containerRef.current, { const renderer = new Sigma(graph, containerRef.current, {
renderLabels: true, renderLabels: true,
labelColor: { color: "#cbd5e1" }, labelColor: { color: "#e2e8f0" },
labelSize: 11, labelSize: 11,
labelWeight: "500", labelWeight: "600",
labelDensity: 0.6, labelDensity: 0.5,
labelRenderedSizeThreshold: N > 120 ? 12 : 0, // bei vielen Knoten nur große labeln (entklumpen) labelRenderedSizeThreshold: N > 120 ? 12 : 0, // bei vielen Knoten nur große labeln (entklumpen)
defaultEdgeColor: EDGE, defaultEdgeColor: EDGE,
minCameraRatio: 0.1, minCameraRatio: 0.1,
maxCameraRatio: 4, maxCameraRatio: 4,
nodeReducer: (id, attrs) => { nodeReducer: (id, attrs) => {
const a = active.current const a = active.current
if (!a) return attrs const isSel = id === selectedRef.current
if (id === a || graph.areNeighbors(a, id)) return attrs
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: "" } return { ...attrs, color: DIM_NODE, label: "" }
}, },
edgeReducer: (edge, attrs) => { edgeReducer: (edge, attrs) => {
const a = active.current const a = active.current
if (!a) return attrs if (!a) return attrs
const ext = graph.extremities(edge) 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 } return { ...attrs, color: DIM_EDGE }
}, },
}) })
@@ -147,7 +161,8 @@ export function GraphView({ data, onDelete }: { data: MemoryGraph; onDelete: (id
return ( return (
<div className="grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-3"> <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" /> <div ref={containerRef} className="absolute inset-0" />
<button onClick={relayout} title="Knoten neu anordnen" <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"> 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">