Politur: Gedaechnis-Graph-Visualisierung veredelt (Neon-Glow, radialer Hintergrundverlauf und reaktives Zoom-Scaling)
This commit is contained in:
+9
-9
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user