● Zion Boggan
repos/Darwin/face/src/components/organism/DarwinOrganism.tsx
zionboggan.com ↗
321 lines · tsx
History for this file →
1
"use client";
2
 
3
import { useRef, useMemo, useEffect, useState, useCallback } from "react";
4
import { Canvas, useFrame, useThree } from "@react-three/fiber";
5
import { Text, Line } from "@react-three/drei";
6
import * as THREE from "three";
7
import type { HealthResponse, DataSource } from "@/lib/types";
8
 
9
 
10
const SOURCE_CONFIG: Record<DataSource, { label: string; basePos: [number, number, number] }> = {
11
  proxmox:          { label: "PROXMOX",       basePos: [-4.0,  2.2, -0.4] },
12
  "white-pc":       { label: "WHITE-PC",      basePos: [ 3.8,  2.6,  0.2] },
13
  "3060":           { label: "RTX 3060",      basePos: [ 2.8, -1.8,  0.7] },
14
  "browser-history":{ label: "BROWSER",       basePos: [ 0.5,  3.8,  0.3] },
15
  agents:           { label: "AGENTS",        basePos: [-1.8, -3.4,  0.0] },
16
  rag:              { label: "RAG",           basePos: [-3.6, -0.6,  0.9] },
17
  cbm:              { label: "CBM",           basePos: [ 2.4,  1.0, -1.0] },
18
  serena:           { label: "SERENA",        basePos: [-1.0,  2.6, -1.6] },
19
  memgraph:         { label: "MEMGRAPH",      basePos: [ 0.3, -2.6, -1.0] },
20
  inbox:            { label: "INBOX",         basePos: [ 4.4, -0.6, -0.3] },
21
};
22
 
23
const SOURCES = Object.keys(SOURCE_CONFIG) as DataSource[];
24
 
25
const CONNECTIONS: [DataSource, DataSource][] = [
26
  ["rag", "proxmox"],
27
  ["rag", "agents"],
28
  ["rag", "cbm"],
29
  ["cbm", "serena"],
30
  ["cbm", "proxmox"],
31
  ["agents", "memgraph"],
32
  ["agents", "inbox"],
33
  ["serena", "proxmox"],
34
  ["memgraph", "rag"],
35
  ["inbox", "agents"],
36
  ["3060", "agents"],
37
  ["white-pc", "rag"],
38
  ["browser-history", "memgraph"],
39
];
40
 
41
 
42
const TEAL = new THREE.Color("#16e0c8");
43
const TEAL_DIM = new THREE.Color("#0d9e8e");
44
const GREY = new THREE.Color("#3a3a3a");
45
const DEAD = new THREE.Color("#1a1a1a");
46
 
47
function healthColor(staleness_h: number, status: "up" | "down"): THREE.Color {
48
  if (status === "down") return DEAD;
49
  if (staleness_h < 2) return TEAL;
50
  if (staleness_h < 12) return TEAL_DIM;
51
  if (staleness_h < 24) return GREY;
52
  return DEAD;
53
}
54
 
55
function healthScale(staleness_h: number, status: "up" | "down"): number {
56
  if (status === "down") return 0.3;
57
  if (staleness_h > 24) return 0.4;
58
  if (staleness_h > 12) return 0.7;
59
  return 1.0;
60
}
61
 
62
 
63
interface NodeProps {
64
  id: DataSource;
65
  targetPos: [number, number, number];
66
  color: THREE.Color;
67
  scale: number;
68
  label: string;
69
  cursorWorld: React.MutableRefObject<THREE.Vector3>;
70
  spawnProgress: number;
71
  isStale: boolean;
72
  collapse: React.MutableRefObject<number>;
73
}
74
 
75
function OrgNode({ id, targetPos, color, scale, label, cursorWorld, spawnProgress, isStale, collapse }: NodeProps) {
76
  const meshRef = useRef<THREE.Mesh>(null);
77
  const currentPos = useRef(new THREE.Vector3(0, 0, 0));
78
  const target = useMemo(() => new THREE.Vector3(...targetPos), [targetPos]);
79
  const glowRef = useRef<THREE.PointLight>(null);
80
  const timeRef = useRef(Math.random() * Math.PI * 2);
81
 
82
  useFrame((_, delta) => {
83
    if (!meshRef.current) return;
84
    timeRef.current += delta;
85
 
86
    const attraction = new THREE.Vector3().copy(cursorWorld.current).sub(target);
87
    const dist = attraction.length();
88
    const pull = dist < 4 ? (1 - dist / 4) * 0.4 : 0;
89
 
90
    const pullTarget = new THREE.Vector3()
91
      .copy(target)
92
      .addScaledVector(attraction.normalize(), pull * -1);
93
 
94
    pullTarget.multiplyScalar(1 - collapse.current * 0.82);
95
 
96
    currentPos.current.lerp(pullTarget.multiplyScalar(spawnProgress), 0.08);
97
    meshRef.current.position.copy(currentPos.current);
98
 
99
    const breathe = 1 + Math.sin(timeRef.current * 0.7 + timeRef.current) * 0.05;
100
    const finalScale = scale * breathe * spawnProgress * (1 - collapse.current * 0.35);
101
    meshRef.current.scale.setScalar(finalScale);
102
 
103
    if (glowRef.current) {
104
      const intensity = isStale ? 0 : 0.5 + Math.sin(timeRef.current * 1.2) * 0.2;
105
      glowRef.current.intensity = intensity * spawnProgress;
106
    }
107
  });
108
 
109
  return (
110
    <group>
111
      <mesh ref={meshRef}>
112
        {}
113
        <sphereGeometry args={[0.32, 24, 24]} />
114
        <meshStandardMaterial
115
          color={color}
116
          emissive={color}
117
          emissiveIntensity={isStale ? 0.08 : 0.8}
118
          roughness={0.15}
119
          metalness={0.4}
120
        />
121
      </mesh>
122
      <pointLight
123
        ref={glowRef}
124
        color={color}
125
        intensity={0}
126
        distance={3.5}
127
        position={[...targetPos] as [number, number, number]}
128
      />
129
      <Text
130
        position={[targetPos[0], targetPos[1] + 0.55, targetPos[2]]}
131
        fontSize={0.15}
132
        letterSpacing={0.06}
133
        color={isStale ? "#3a3a3a" : "#16e0c8"}
134
        anchorX="center"
135
        anchorY="bottom"
136
        outlineWidth={0}
137
        material-toneMapped={false}
138
      >
139
        {label}
140
      </Text>
141
    </group>
142
  );
143
}
144
 
145
 
146
interface SceneProps {
147
  healthData: HealthResponse | null;
148
  focusMode: boolean;
149
}
150
 
151
function OrganismScene({ healthData, focusMode }: SceneProps) {
152
  const { camera, gl } = useThree();
153
  const cursorWorld = useRef(new THREE.Vector3(0, 0, 0));
154
  const [spawnProgress, setSpawnProgress] = useState(0);
155
  const startTime = useRef(Date.now());
156
  const collapse = useRef(0);
157
  const centerGlowRef = useRef<THREE.PointLight>(null);
158
 
159
  useEffect(() => {
160
    camera.position.set(0, 0, 6);
161
  }, [camera]);
162
 
163
  useEffect(() => {
164
    let rafId: number;
165
    function tick() {
166
      const elapsed = (Date.now() - startTime.current) / 1200;
167
      const t = Math.min(elapsed, 1);
168
      const eased = t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
169
      setSpawnProgress(eased);
170
      if (t < 1) rafId = requestAnimationFrame(tick);
171
    }
172
    rafId = requestAnimationFrame(tick);
173
    return () => cancelAnimationFrame(rafId);
174
  }, []);
175
 
176
  useEffect(() => {
177
    const canvas = gl.domElement;
178
    function onMouseMove(e: MouseEvent) {
179
      const rect = canvas.getBoundingClientRect();
180
      const nx = ((e.clientX - rect.left) / rect.width) * 2 - 1;
181
      const ny = -((e.clientY - rect.top) / rect.height) * 2 + 1;
182
      const vec = new THREE.Vector3(nx, ny, 0.5);
183
      vec.unproject(camera);
184
      vec.sub(camera.position).normalize();
185
      const dist = -camera.position.z / vec.z;
186
      const pos = new THREE.Vector3().addVectors(camera.position, vec.multiplyScalar(dist));
187
      cursorWorld.current.copy(pos);
188
    }
189
    canvas.addEventListener("mousemove", onMouseMove, { passive: true });
190
    return () => canvas.removeEventListener("mousemove", onMouseMove);
191
  }, [camera, gl]);
192
 
193
  const healthMap = useMemo(() => {
194
    const map = new Map<string, { staleness_h: number; status: "up" | "down" }>();
195
    if (healthData) {
196
      for (const eng of healthData.engines) {
197
        map.set(eng.engine, { staleness_h: eng.staleness_h, status: eng.status });
198
      }
199
    }
200
    return map;
201
  }, [healthData]);
202
 
203
  const groupRef = useRef<THREE.Group>(null);
204
  useFrame((_, delta) => {
205
    if (groupRef.current) {
206
      groupRef.current.rotation.y += delta * 0.04;
207
      groupRef.current.rotation.x += delta * 0.01;
208
    }
209
    const collapseTarget = focusMode ? 1 : 0;
210
    collapse.current += (collapseTarget - collapse.current) * Math.min(1, delta * 4.5);
211
    if (centerGlowRef.current) {
212
      centerGlowRef.current.intensity = (1.5 + collapse.current * 2.5) * spawnProgress;
213
    }
214
  });
215
 
216
  return (
217
    <>
218
      {}
219
      <ambientLight intensity={0.25} />
220
      <pointLight position={[0, 0, 4]} intensity={0.6} color="#ffffff" />
221
      <pointLight position={[-4, 3, 2]} intensity={0.3} color="#16e0c8" distance={12} />
222
      <pointLight ref={centerGlowRef} position={[0, 0, 0]} intensity={2.0} color="#16e0c8" distance={10} />
223
 
224
      <group ref={groupRef}>
225
        {}
226
        {CONNECTIONS.map(([a, b], i) => {
227
          const aPos = SOURCE_CONFIG[a].basePos;
228
          const bPos = SOURCE_CONFIG[b].basePos;
229
          const aHealth = healthMap.get(a);
230
          const bHealth = healthMap.get(b);
231
          const bothHealthy =
232
            (!aHealth || aHealth.staleness_h < 12) &&
233
            (!bHealth || bHealth.staleness_h < 12);
234
          return (
235
            <Line
236
              key={i}
237
              points={[aPos, bPos]}
238
              color={bothHealthy ? "#16e0c8" : "#222"}
239
              lineWidth={bothHealthy ? 0.8 : 0.4}
240
              transparent
241
              opacity={bothHealthy ? 0.35 * spawnProgress : 0.1 * spawnProgress}
242
            />
243
          );
244
        })}
245
 
246
        {}
247
        {SOURCES.map((id) => {
248
          const cfg = SOURCE_CONFIG[id];
249
          const h = healthMap.get(id) ?? { staleness_h: 0, status: "up" as const };
250
          return (
251
            <OrgNode
252
              key={id}
253
              id={id}
254
              targetPos={cfg.basePos}
255
              color={healthColor(h.staleness_h, h.status)}
256
              scale={healthScale(h.staleness_h, h.status)}
257
              label={cfg.label}
258
              cursorWorld={cursorWorld}
259
              spawnProgress={spawnProgress}
260
              isStale={h.staleness_h > 12 || h.status === "down"}
261
              collapse={collapse}
262
            />
263
          );
264
        })}
265
      </group>
266
    </>
267
  );
268
}
269
 
270
 
271
interface DarwinOrganismProps {
272
  healthData: HealthResponse | null;
273
  focusMode?: boolean;
274
}
275
 
276
function RenderGate() {
277
  const invalidate = useThree((s) => s.invalidate);
278
  const gl = useThree((s) => s.gl);
279
  const visibleRef = useRef(true);
280
  const accRef = useRef(0);
281
 
282
  useEffect(() => {
283
    const el = gl.domElement;
284
    const io = new IntersectionObserver(
285
      (entries) => {
286
        const entry = entries[0];
287
        visibleRef.current = entry.isIntersecting;
288
        if (entry.isIntersecting) invalidate();
289
      },
290
      { threshold: 0 }
291
    );
292
    io.observe(el);
293
    return () => io.disconnect();
294
  }, [gl, invalidate]);
295
 
296
  useFrame((_, delta) => {
297
    if (!visibleRef.current) return;
298
    accRef.current += delta;
299
    if (accRef.current >= 1 / 30) {
300
      accRef.current = 0;
301
      invalidate();
302
    }
303
  });
304
 
305
  return null;
306
}
307
 
308
export default function DarwinOrganism({ healthData, focusMode = false }: DarwinOrganismProps) {
309
  return (
310
    <Canvas
311
      frameloop="demand"
312
      dpr={[1, 2]}
313
      gl={{ antialias: true, alpha: true }}
314
      style={{ background: "transparent" }}
315
      camera={{ fov: 60, near: 0.1, far: 100 }}
316
    >
317
      <RenderGate />
318
      <OrganismScene healthData={healthData} focusMode={focusMode} />
319
    </Canvas>
320
  );
321
}