claude-cortex 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dashboard/README.md +36 -0
  2. package/dashboard/components.json +22 -0
  3. package/dashboard/eslint.config.mjs +18 -0
  4. package/dashboard/next.config.ts +7 -0
  5. package/dashboard/package-lock.json +7784 -0
  6. package/dashboard/package.json +42 -0
  7. package/dashboard/postcss.config.mjs +7 -0
  8. package/dashboard/public/file.svg +1 -0
  9. package/dashboard/public/globe.svg +1 -0
  10. package/dashboard/public/next.svg +1 -0
  11. package/dashboard/public/vercel.svg +1 -0
  12. package/dashboard/public/window.svg +1 -0
  13. package/dashboard/src/app/favicon.ico +0 -0
  14. package/dashboard/src/app/globals.css +125 -0
  15. package/dashboard/src/app/layout.tsx +35 -0
  16. package/dashboard/src/app/page.tsx +338 -0
  17. package/dashboard/src/components/Providers.tsx +27 -0
  18. package/dashboard/src/components/brain/ActivityPulseSystem.tsx +229 -0
  19. package/dashboard/src/components/brain/BrainMesh.tsx +118 -0
  20. package/dashboard/src/components/brain/BrainRegions.tsx +254 -0
  21. package/dashboard/src/components/brain/BrainScene.tsx +255 -0
  22. package/dashboard/src/components/brain/CategoryLabels.tsx +103 -0
  23. package/dashboard/src/components/brain/CoreSphere.tsx +215 -0
  24. package/dashboard/src/components/brain/DataFlowParticles.tsx +123 -0
  25. package/dashboard/src/components/brain/DataStreamRings.tsx +161 -0
  26. package/dashboard/src/components/brain/ElectronFlow.tsx +323 -0
  27. package/dashboard/src/components/brain/HolographicGrid.tsx +235 -0
  28. package/dashboard/src/components/brain/MemoryLinks.tsx +271 -0
  29. package/dashboard/src/components/brain/MemoryNode.tsx +245 -0
  30. package/dashboard/src/components/brain/NeuralPathways.tsx +441 -0
  31. package/dashboard/src/components/brain/SynapseNodes.tsx +306 -0
  32. package/dashboard/src/components/brain/TimelineControls.tsx +205 -0
  33. package/dashboard/src/components/chip/ChipScene.tsx +497 -0
  34. package/dashboard/src/components/chip/ChipSubstrate.tsx +238 -0
  35. package/dashboard/src/components/chip/CortexCore.tsx +210 -0
  36. package/dashboard/src/components/chip/DataBus.tsx +416 -0
  37. package/dashboard/src/components/chip/MemoryCell.tsx +225 -0
  38. package/dashboard/src/components/chip/MemoryGrid.tsx +328 -0
  39. package/dashboard/src/components/chip/QuantumCell.tsx +316 -0
  40. package/dashboard/src/components/chip/SectionLabel.tsx +113 -0
  41. package/dashboard/src/components/chip/index.ts +14 -0
  42. package/dashboard/src/components/controls/ControlPanel.tsx +100 -0
  43. package/dashboard/src/components/dashboard/StatsPanel.tsx +164 -0
  44. package/dashboard/src/components/debug/ActivityLog.tsx +238 -0
  45. package/dashboard/src/components/debug/DebugPanel.tsx +101 -0
  46. package/dashboard/src/components/debug/QueryTester.tsx +192 -0
  47. package/dashboard/src/components/debug/RelationshipGraph.tsx +403 -0
  48. package/dashboard/src/components/debug/SqlConsole.tsx +313 -0
  49. package/dashboard/src/components/memory/MemoryDetail.tsx +325 -0
  50. package/dashboard/src/components/ui/button.tsx +62 -0
  51. package/dashboard/src/components/ui/card.tsx +92 -0
  52. package/dashboard/src/components/ui/input.tsx +21 -0
  53. package/dashboard/src/hooks/useDebouncedValue.ts +24 -0
  54. package/dashboard/src/hooks/useMemories.ts +276 -0
  55. package/dashboard/src/hooks/useSuggestions.ts +46 -0
  56. package/dashboard/src/lib/category-colors.ts +84 -0
  57. package/dashboard/src/lib/position-algorithm.ts +177 -0
  58. package/dashboard/src/lib/simplex-noise.ts +217 -0
  59. package/dashboard/src/lib/store.ts +88 -0
  60. package/dashboard/src/lib/utils.ts +6 -0
  61. package/dashboard/src/lib/websocket.ts +216 -0
  62. package/dashboard/src/types/memory.ts +73 -0
  63. package/dashboard/tsconfig.json +34 -0
  64. package/dist/api/control.d.ts +27 -0
  65. package/dist/api/control.d.ts.map +1 -0
  66. package/dist/api/control.js +60 -0
  67. package/dist/api/control.js.map +1 -0
  68. package/dist/api/visualization-server.d.ts.map +1 -1
  69. package/dist/api/visualization-server.js +109 -2
  70. package/dist/api/visualization-server.js.map +1 -1
  71. package/dist/index.d.ts +2 -1
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +80 -4
  74. package/dist/index.js.map +1 -1
  75. package/dist/memory/store.d.ts +6 -0
  76. package/dist/memory/store.d.ts.map +1 -1
  77. package/dist/memory/store.js +14 -0
  78. package/dist/memory/store.js.map +1 -1
  79. package/package.json +7 -3
@@ -0,0 +1,255 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Brain Scene
5
+ * Main 3D visualization of the memory brain
6
+ * Composes all brain visualization components into a cohesive scene
7
+ *
8
+ * Features:
9
+ * - Real-time activity pulses on memory events
10
+ * - Category region labels
11
+ * - Color modes: category, health (decay), age
12
+ * - Timeline filtering
13
+ */
14
+
15
+ import { Suspense, useMemo, useState, useCallback } from 'react';
16
+ import { Canvas } from '@react-three/fiber';
17
+ import { OrbitControls } from '@react-three/drei';
18
+ // Post-processing disabled for cleaner appearance
19
+ // import { EffectComposer, Bloom, Vignette, ChromaticAberration, Noise } from '@react-three/postprocessing';
20
+ import { Memory, MemoryLink, MemoryCategory } from '@/types/memory';
21
+ import { MemoryNode } from './MemoryNode';
22
+ import { MemoryLinks } from './MemoryLinks';
23
+ import { BrainMesh } from './BrainMesh';
24
+ import { ActivityPulseSystem, Pulse } from './ActivityPulseSystem';
25
+ import { TimelineControls } from './TimelineControls';
26
+ // Removed for cleaner design: BrainRegions, Stars
27
+ import { calculateMemoryPosition } from '@/lib/position-algorithm';
28
+ import { useMemoryWebSocket } from '@/lib/websocket';
29
+
30
+ type ColorMode = 'category' | 'health' | 'age' | 'holographic';
31
+
32
+ interface BrainSceneProps {
33
+ memories: Memory[];
34
+ links?: MemoryLink[];
35
+ selectedMemory: Memory | null;
36
+ onSelectMemory: (memory: Memory | null) => void;
37
+ }
38
+
39
+ interface BrainContentProps extends BrainSceneProps {
40
+ colorMode: ColorMode;
41
+ pulses: Pulse[];
42
+ onPulseComplete: (id: number) => void;
43
+ memoryCategoryCounts: Record<string, number>;
44
+ }
45
+
46
+ function BrainContent({
47
+ memories = [],
48
+ links = [],
49
+ selectedMemory,
50
+ onSelectMemory,
51
+ colorMode,
52
+ pulses,
53
+ onPulseComplete,
54
+ memoryCategoryCounts,
55
+ }: BrainContentProps) {
56
+ // Calculate positions for all memories (deduplicate by ID to prevent React key errors)
57
+ const memoryPositions = useMemo(() => {
58
+ if (!memories || memories.length === 0) return [];
59
+ // Use Map to deduplicate by memory ID
60
+ const uniqueMemories = new Map(memories.map(m => [m.id, m]));
61
+ return Array.from(uniqueMemories.values()).map((memory) => ({
62
+ memory,
63
+ position: calculateMemoryPosition(memory),
64
+ }));
65
+ }, [memories]);
66
+
67
+ // Create a map for quick position lookup by memory ID
68
+ const positionMap = useMemo(() => {
69
+ const map = new Map<number, { x: number; y: number; z: number }>();
70
+ memoryPositions.forEach(({ memory, position }) => {
71
+ map.set(memory.id, position);
72
+ });
73
+ return map;
74
+ }, [memoryPositions]);
75
+
76
+ return (
77
+ <>
78
+ {/* Simple, even lighting for maximum clarity */}
79
+ <ambientLight intensity={0.6} color="#ffffff" />
80
+ <directionalLight position={[5, 5, 5]} intensity={0.3} color="#ffffff" />
81
+
82
+ {/* Ghost brain outline - barely visible context */}
83
+ <BrainMesh opacity={0.05} showWireframe={true} pulseIntensity={0} />
84
+
85
+ {/* Neural connections - the main visual feature */}
86
+ <MemoryLinks
87
+ memories={memories}
88
+ links={links}
89
+ memoryPositions={positionMap}
90
+ />
91
+
92
+ {/* Memory nodes - clear, colored spheres */}
93
+ {memoryPositions.map(({ memory, position }) => (
94
+ <MemoryNode
95
+ key={memory.id}
96
+ memory={memory}
97
+ position={[position.x, position.y, position.z]}
98
+ onSelect={onSelectMemory}
99
+ isSelected={selectedMemory?.id === memory.id}
100
+ colorMode={colorMode}
101
+ />
102
+ ))}
103
+
104
+ {/* Activity pulses for real-time events */}
105
+ <ActivityPulseSystem pulses={pulses} onPulseComplete={onPulseComplete} />
106
+
107
+ {/* Camera controls - user controlled, no auto-rotate */}
108
+ <OrbitControls
109
+ enablePan={true}
110
+ enableZoom={true}
111
+ enableRotate={true}
112
+ minDistance={5}
113
+ maxDistance={25}
114
+ autoRotate={false}
115
+ dampingFactor={0.05}
116
+ enableDamping
117
+ />
118
+ </>
119
+ );
120
+ }
121
+
122
+ export function BrainScene({
123
+ memories = [],
124
+ links = [],
125
+ selectedMemory,
126
+ onSelectMemory,
127
+ }: BrainSceneProps) {
128
+ const [colorMode, setColorMode] = useState<ColorMode>('category');
129
+ const [pulses, setPulses] = useState<Pulse[]>([]);
130
+ const [timeRange, setTimeRange] = useState<{ start: Date | null; end: Date | null }>({
131
+ start: null,
132
+ end: null,
133
+ });
134
+ const pulseIdRef = { current: 0 };
135
+
136
+ // Calculate memory counts by category
137
+ const memoryCategoryCounts = useMemo(() => {
138
+ const counts: Record<string, number> = {};
139
+ memories.forEach((m) => {
140
+ counts[m.category] = (counts[m.category] || 0) + 1;
141
+ });
142
+ return counts;
143
+ }, [memories]);
144
+
145
+ // Filter memories by time range
146
+ const filteredMemories = useMemo(() => {
147
+ if (!timeRange.start && !timeRange.end) return memories;
148
+
149
+ return memories.filter((m) => {
150
+ const createdAt = new Date(m.createdAt).getTime();
151
+ if (timeRange.start && createdAt < timeRange.start.getTime()) return false;
152
+ if (timeRange.end && createdAt > timeRange.end.getTime()) return false;
153
+ return true;
154
+ });
155
+ }, [memories, timeRange]);
156
+
157
+ // Create position map for pulse positioning
158
+ const positionMap = useMemo(() => {
159
+ const map = new Map<number, { x: number; y: number; z: number }>();
160
+ memories.forEach((memory) => {
161
+ map.set(memory.id, calculateMemoryPosition(memory));
162
+ });
163
+ return map;
164
+ }, [memories]);
165
+
166
+ // Create a pulse for memory events
167
+ const createPulse = useCallback(
168
+ (type: Pulse['type'], memoryId: number, targetMemoryId?: number) => {
169
+ const position = positionMap.get(memoryId);
170
+ if (!position) return;
171
+
172
+ const targetPosition = targetMemoryId ? positionMap.get(targetMemoryId) : undefined;
173
+
174
+ const pulse: Pulse = {
175
+ id: pulseIdRef.current++,
176
+ type,
177
+ position: [position.x, position.y, position.z],
178
+ targetPosition: targetPosition
179
+ ? [targetPosition.x, targetPosition.y, targetPosition.z]
180
+ : undefined,
181
+ startTime: Date.now(),
182
+ duration: type === 'created' ? 2000 : type === 'accessed' ? 1000 : 1500,
183
+ color: type === 'created' ? '#22c55e' : type === 'accessed' ? '#3b82f6' : '#a855f7',
184
+ };
185
+
186
+ setPulses((prev) => [...prev, pulse]);
187
+ },
188
+ [positionMap]
189
+ );
190
+
191
+ // Handle pulse completion
192
+ const handlePulseComplete = useCallback((id: number) => {
193
+ setPulses((prev) => prev.filter((p) => p.id !== id));
194
+ }, []);
195
+
196
+ // Listen for WebSocket events to trigger pulses
197
+ useMemoryWebSocket({
198
+ onMessage: (event) => {
199
+ const data = event.data as Record<string, unknown> | undefined;
200
+ switch (event.type) {
201
+ case 'memory_created':
202
+ if (data?.memoryId) createPulse('created', data.memoryId as number);
203
+ break;
204
+ case 'memory_accessed':
205
+ if (data?.memoryId) createPulse('accessed', data.memoryId as number);
206
+ break;
207
+ case 'link_discovered':
208
+ if (data?.sourceId && data?.targetId)
209
+ createPulse('linked', data.sourceId as number, data.targetId as number);
210
+ break;
211
+ }
212
+ },
213
+ });
214
+
215
+ return (
216
+ <div className="w-full h-full bg-slate-950">
217
+ <Canvas
218
+ camera={{ position: [0, 2, 12], fov: 55 }}
219
+ gl={{ antialias: true, alpha: true }}
220
+ onClick={() => onSelectMemory(null)}
221
+ >
222
+ <Suspense fallback={null}>
223
+ <BrainContent
224
+ memories={filteredMemories}
225
+ links={links}
226
+ selectedMemory={selectedMemory}
227
+ onSelectMemory={onSelectMemory}
228
+ colorMode={colorMode}
229
+ pulses={pulses}
230
+ onPulseComplete={handlePulseComplete}
231
+ memoryCategoryCounts={memoryCategoryCounts}
232
+ />
233
+ </Suspense>
234
+ </Canvas>
235
+
236
+ {/* Simple info overlay */}
237
+ <div className="absolute bottom-4 left-4 bg-slate-900/90 border border-slate-700 rounded-lg p-3 text-xs backdrop-blur-sm">
238
+ <p className="text-slate-300">
239
+ {filteredMemories.length} memories
240
+ </p>
241
+ <p className="text-slate-500 mt-1">
242
+ Click to select • Drag to rotate
243
+ </p>
244
+ </div>
245
+
246
+ {/* Timeline and color controls */}
247
+ <TimelineControls
248
+ memories={memories}
249
+ onTimeRangeChange={setTimeRange}
250
+ colorMode={colorMode}
251
+ onColorModeChange={setColorMode}
252
+ />
253
+ </div>
254
+ );
255
+ }
@@ -0,0 +1,103 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Category Labels
5
+ *
6
+ * Shows labels around the brain for each category region.
7
+ * Categories are arranged in a circle like a clock face.
8
+ */
9
+
10
+ import { useMemo } from 'react';
11
+ import { Html } from '@react-three/drei';
12
+ import { MemoryCategory } from '@/types/memory';
13
+ import { getCategoryColor } from '@/lib/category-colors';
14
+
15
+ // Category positions (angles in degrees, matching position-algorithm.ts)
16
+ const CATEGORY_ANGLES: Record<MemoryCategory, number> = {
17
+ architecture: 0,
18
+ pattern: 36,
19
+ preference: 72,
20
+ error: 108,
21
+ context: 144,
22
+ learning: 180,
23
+ todo: 216,
24
+ note: 252,
25
+ relationship: 288,
26
+ custom: 324,
27
+ };
28
+
29
+ // Category icons
30
+ const CATEGORY_ICONS: Record<MemoryCategory, string> = {
31
+ architecture: '🏗️',
32
+ pattern: '🔄',
33
+ preference: '⚙️',
34
+ error: '🐛',
35
+ context: '📍',
36
+ learning: '💡',
37
+ todo: '✅',
38
+ note: '📝',
39
+ relationship: '🔗',
40
+ custom: '📦',
41
+ };
42
+
43
+ interface CategoryLabelsProps {
44
+ memoryCounts: Record<string, number>;
45
+ radius?: number;
46
+ showCounts?: boolean;
47
+ }
48
+
49
+ export function CategoryLabels({
50
+ memoryCounts,
51
+ radius = 5.5,
52
+ showCounts = true,
53
+ }: CategoryLabelsProps) {
54
+ const labels = useMemo(() => {
55
+ return (Object.keys(CATEGORY_ANGLES) as MemoryCategory[]).map((category) => {
56
+ const angle = CATEGORY_ANGLES[category] * (Math.PI / 180);
57
+ const x = radius * Math.cos(angle);
58
+ const z = radius * Math.sin(angle);
59
+ const count = memoryCounts[category] || 0;
60
+ const color = getCategoryColor(category);
61
+
62
+ return {
63
+ category,
64
+ position: [x, 0, z] as [number, number, number],
65
+ count,
66
+ color,
67
+ icon: CATEGORY_ICONS[category],
68
+ };
69
+ });
70
+ }, [memoryCounts, radius]);
71
+
72
+ return (
73
+ <group name="category-labels">
74
+ {labels.map(({ category, position, count, color, icon }) => (
75
+ <Html
76
+ key={category}
77
+ position={position}
78
+ center
79
+ style={{ pointerEvents: 'none' }}
80
+ distanceFactor={12}
81
+ >
82
+ <div
83
+ className="flex flex-col items-center gap-0.5 opacity-60 hover:opacity-100 transition-opacity"
84
+ style={{ color }}
85
+ >
86
+ <span className="text-lg">{icon}</span>
87
+ <span className="text-[10px] font-medium capitalize whitespace-nowrap">
88
+ {category}
89
+ </span>
90
+ {showCounts && count > 0 && (
91
+ <span
92
+ className="text-[9px] px-1.5 py-0.5 rounded-full"
93
+ style={{ backgroundColor: color + '30' }}
94
+ >
95
+ {count}
96
+ </span>
97
+ )}
98
+ </div>
99
+ </Html>
100
+ ))}
101
+ </group>
102
+ );
103
+ }
@@ -0,0 +1,215 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Core Sphere
5
+ * Glowing central energy core - the "heart" of the AI brain
6
+ *
7
+ * Features:
8
+ * - Bright central sphere with multiple glow layers
9
+ * - Pulsing animation tied to activity level
10
+ * - Color gradient from deep orange (#FF8C00) to bright gold (#FFD700)
11
+ * - Additive blending for realistic glow effect
12
+ */
13
+
14
+ import { useRef, useMemo, useEffect } from 'react';
15
+ import { useFrame } from '@react-three/fiber';
16
+ import * as THREE from 'three';
17
+
18
+ // Shared geometries to prevent memory leaks
19
+ const CORE_GEOMETRY = new THREE.SphereGeometry(1, 32, 32);
20
+ const GLOW_GEOMETRY = new THREE.SphereGeometry(1, 24, 24);
21
+
22
+ interface CoreSphereProps {
23
+ /** Activity level 0-1, affects pulse intensity and speed */
24
+ activity?: number;
25
+ /** Base radius of the core sphere */
26
+ radius?: number;
27
+ /** Position of the core */
28
+ position?: [number, number, number];
29
+ }
30
+
31
+ export function CoreSphere({
32
+ activity = 0.5,
33
+ radius = 0.4,
34
+ position = [0, 0, 0],
35
+ }: CoreSphereProps) {
36
+ const coreRef = useRef<THREE.Mesh>(null);
37
+ const glow1Ref = useRef<THREE.Mesh>(null);
38
+ const glow2Ref = useRef<THREE.Mesh>(null);
39
+ const glow3Ref = useRef<THREE.Mesh>(null);
40
+ const glow4Ref = useRef<THREE.Mesh>(null);
41
+
42
+ // Core material - brightest, deep orange
43
+ const coreMaterial = useMemo(
44
+ () =>
45
+ new THREE.MeshBasicMaterial({
46
+ color: '#FF8C00',
47
+ transparent: true,
48
+ opacity: 0.95,
49
+ depthWrite: false,
50
+ }),
51
+ []
52
+ );
53
+
54
+ // Inner glow layer 1 - orange-gold
55
+ const glow1Material = useMemo(
56
+ () =>
57
+ new THREE.MeshBasicMaterial({
58
+ color: '#FFA500',
59
+ transparent: true,
60
+ opacity: 0.15,
61
+ depthWrite: false,
62
+ blending: THREE.AdditiveBlending,
63
+ }),
64
+ []
65
+ );
66
+
67
+ // Glow layer 2 - transitioning to gold
68
+ const glow2Material = useMemo(
69
+ () =>
70
+ new THREE.MeshBasicMaterial({
71
+ color: '#FFB700',
72
+ transparent: true,
73
+ opacity: 0.08,
74
+ depthWrite: false,
75
+ blending: THREE.AdditiveBlending,
76
+ }),
77
+ []
78
+ );
79
+
80
+ // Glow layer 3 - lighter gold
81
+ const glow3Material = useMemo(
82
+ () =>
83
+ new THREE.MeshBasicMaterial({
84
+ color: '#FFC500',
85
+ transparent: true,
86
+ opacity: 0.04,
87
+ depthWrite: false,
88
+ blending: THREE.AdditiveBlending,
89
+ }),
90
+ []
91
+ );
92
+
93
+ // Outer glow layer 4 - brightest gold, most diffuse
94
+ const glow4Material = useMemo(
95
+ () =>
96
+ new THREE.MeshBasicMaterial({
97
+ color: '#FFD700',
98
+ transparent: true,
99
+ opacity: 0.02,
100
+ depthWrite: false,
101
+ blending: THREE.AdditiveBlending,
102
+ }),
103
+ []
104
+ );
105
+
106
+ // Cleanup materials on unmount
107
+ useEffect(() => {
108
+ return () => {
109
+ coreMaterial.dispose();
110
+ glow1Material.dispose();
111
+ glow2Material.dispose();
112
+ glow3Material.dispose();
113
+ glow4Material.dispose();
114
+ };
115
+ }, [coreMaterial, glow1Material, glow2Material, glow3Material, glow4Material]);
116
+
117
+ // Animation - pulsing "breathing" effect
118
+ useFrame((state) => {
119
+ if (!coreRef.current) return;
120
+
121
+ const time = state.clock.elapsedTime;
122
+
123
+ // Base pulse speed increases with activity (0.8 to 3.0 Hz)
124
+ const pulseSpeed = 0.8 + activity * 2.2;
125
+
126
+ // Primary breathing pulse
127
+ const primaryPulse = Math.sin(time * pulseSpeed) * 0.5 + 0.5;
128
+
129
+ // Secondary faster micro-pulse for energy feel
130
+ const microPulse = Math.sin(time * pulseSpeed * 3) * 0.15 + 0.85;
131
+
132
+ // Combined pulse with activity influence
133
+ const combinedPulse = primaryPulse * (0.7 + activity * 0.3) * microPulse;
134
+
135
+ // Core sphere - subtle scale pulsing
136
+ const coreScale = radius * (1 + combinedPulse * 0.15);
137
+ coreRef.current.scale.setScalar(coreScale);
138
+ coreMaterial.opacity = 0.85 + combinedPulse * 0.1;
139
+
140
+ // Glow layer 1 - larger, synced with core
141
+ if (glow1Ref.current) {
142
+ const glow1Scale = radius * 1.6 * (1 + combinedPulse * 0.1);
143
+ glow1Ref.current.scale.setScalar(glow1Scale);
144
+ glow1Material.opacity = (0.1 + combinedPulse * 0.06) * (0.6 + activity * 0.4);
145
+ }
146
+
147
+ // Glow layer 2 - slightly phase-shifted for depth
148
+ if (glow2Ref.current) {
149
+ const phase2 = Math.sin(time * pulseSpeed - 0.3) * 0.5 + 0.5;
150
+ const glow2Scale = radius * 2.0 * (1 + phase2 * 0.08);
151
+ glow2Ref.current.scale.setScalar(glow2Scale);
152
+ glow2Material.opacity = (0.05 + phase2 * 0.04) * (0.5 + activity * 0.5);
153
+ }
154
+
155
+ // Glow layer 3 - more phase shift, slower feel
156
+ if (glow3Ref.current) {
157
+ const phase3 = Math.sin(time * pulseSpeed * 0.7 - 0.6) * 0.5 + 0.5;
158
+ const glow3Scale = radius * 2.4 * (1 + phase3 * 0.06);
159
+ glow3Ref.current.scale.setScalar(glow3Scale);
160
+ glow3Material.opacity = (0.025 + phase3 * 0.02) * (0.4 + activity * 0.6);
161
+ }
162
+
163
+ // Glow layer 4 - outermost, slowest, most ethereal
164
+ if (glow4Ref.current) {
165
+ const phase4 = Math.sin(time * pulseSpeed * 0.5 - 0.9) * 0.5 + 0.5;
166
+ const glow4Scale = radius * 3.0 * (1 + phase4 * 0.05);
167
+ glow4Ref.current.scale.setScalar(glow4Scale);
168
+ glow4Material.opacity = (0.01 + phase4 * 0.015) * (0.3 + activity * 0.7);
169
+ }
170
+ });
171
+
172
+ return (
173
+ <group position={position} name="core-sphere">
174
+ {/* Outermost glow layer 4 - brightest gold, most diffuse */}
175
+ <mesh
176
+ ref={glow4Ref}
177
+ geometry={GLOW_GEOMETRY}
178
+ material={glow4Material}
179
+ scale={radius * 3.0}
180
+ />
181
+
182
+ {/* Glow layer 3 - lighter gold */}
183
+ <mesh
184
+ ref={glow3Ref}
185
+ geometry={GLOW_GEOMETRY}
186
+ material={glow3Material}
187
+ scale={radius * 2.4}
188
+ />
189
+
190
+ {/* Glow layer 2 - transitioning gold */}
191
+ <mesh
192
+ ref={glow2Ref}
193
+ geometry={GLOW_GEOMETRY}
194
+ material={glow2Material}
195
+ scale={radius * 2.0}
196
+ />
197
+
198
+ {/* Inner glow layer 1 - orange-gold */}
199
+ <mesh
200
+ ref={glow1Ref}
201
+ geometry={GLOW_GEOMETRY}
202
+ material={glow1Material}
203
+ scale={radius * 1.6}
204
+ />
205
+
206
+ {/* Core sphere - brightest, deep orange center */}
207
+ <mesh
208
+ ref={coreRef}
209
+ geometry={CORE_GEOMETRY}
210
+ material={coreMaterial}
211
+ scale={radius}
212
+ />
213
+ </group>
214
+ );
215
+ }
@@ -0,0 +1,123 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Data Flow Particles
5
+ * Animated particles flowing between brain regions
6
+ */
7
+
8
+ import { useRef, useMemo } from 'react';
9
+ import { useFrame } from '@react-three/fiber';
10
+ import * as THREE from 'three';
11
+
12
+ interface DataFlowParticlesProps {
13
+ count?: number;
14
+ speed?: number;
15
+ }
16
+
17
+ export function DataFlowParticles({
18
+ count = 150,
19
+ speed = 0.5,
20
+ }: DataFlowParticlesProps) {
21
+ const pointsRef = useRef<THREE.Points>(null);
22
+
23
+ // Initialize particle data
24
+ const { positions, velocities, colors, opacities } = useMemo(() => {
25
+ const pos = new Float32Array(count * 3);
26
+ const vel = new Float32Array(count * 3);
27
+ const col = new Float32Array(count * 3);
28
+ const opa = new Float32Array(count);
29
+
30
+ for (let i = 0; i < count; i++) {
31
+ const i3 = i * 3;
32
+
33
+ // Random position within brain bounds
34
+ pos[i3] = (Math.random() - 0.5) * 8;
35
+ pos[i3 + 1] = (Math.random() - 0.5) * 6;
36
+ pos[i3 + 2] = (Math.random() - 0.5) * 7;
37
+
38
+ // Velocity - flowing from front to back (STM to LTM direction)
39
+ vel[i3] = (Math.random() - 0.5) * 0.01;
40
+ vel[i3 + 1] = (Math.random() - 0.5) * 0.01;
41
+ vel[i3 + 2] = -0.015 - Math.random() * 0.02;
42
+
43
+ // Initial opacity
44
+ opa[i] = 0.3 + Math.random() * 0.4;
45
+
46
+ // Color based on z-position
47
+ updateColor(col, i3, pos[i3 + 2]);
48
+ }
49
+
50
+ return { positions: pos, velocities: vel, colors: col, opacities: opa };
51
+ }, [count]);
52
+
53
+ function updateColor(col: Float32Array, i3: number, z: number) {
54
+ if (z > 1.5) {
55
+ // Short-term region - orange
56
+ col[i3] = 0.976;
57
+ col[i3 + 1] = 0.451;
58
+ col[i3 + 2] = 0.086;
59
+ } else if (z < -1.5) {
60
+ // Long-term region - blue
61
+ col[i3] = 0.231;
62
+ col[i3 + 1] = 0.51;
63
+ col[i3 + 2] = 0.965;
64
+ } else {
65
+ // Episodic region - purple
66
+ col[i3] = 0.545;
67
+ col[i3 + 1] = 0.361;
68
+ col[i3 + 2] = 0.965;
69
+ }
70
+ }
71
+
72
+ useFrame(() => {
73
+ if (!pointsRef.current) return;
74
+
75
+ const posAttr = pointsRef.current.geometry.attributes.position;
76
+ const colAttr = pointsRef.current.geometry.attributes.color;
77
+
78
+ for (let i = 0; i < count; i++) {
79
+ const i3 = i * 3;
80
+
81
+ // Update position
82
+ posAttr.array[i3] += velocities[i3] * speed;
83
+ posAttr.array[i3 + 1] += velocities[i3 + 1] * speed;
84
+ posAttr.array[i3 + 2] += velocities[i3 + 2] * speed;
85
+
86
+ // Reset if out of bounds (wrap around)
87
+ if (posAttr.array[i3 + 2] < -4) {
88
+ posAttr.array[i3 + 2] = 4;
89
+ posAttr.array[i3] = (Math.random() - 0.5) * 8;
90
+ posAttr.array[i3 + 1] = (Math.random() - 0.5) * 6;
91
+ }
92
+
93
+ // Update color based on new position
94
+ updateColor(colAttr.array as Float32Array, i3, posAttr.array[i3 + 2]);
95
+ }
96
+
97
+ posAttr.needsUpdate = true;
98
+ colAttr.needsUpdate = true;
99
+ });
100
+
101
+ return (
102
+ <points ref={pointsRef}>
103
+ <bufferGeometry>
104
+ <bufferAttribute
105
+ attach="attributes-position"
106
+ args={[positions, 3]}
107
+ />
108
+ <bufferAttribute
109
+ attach="attributes-color"
110
+ args={[colors, 3]}
111
+ />
112
+ </bufferGeometry>
113
+ <pointsMaterial
114
+ size={0.06}
115
+ vertexColors
116
+ transparent
117
+ opacity={0.6}
118
+ sizeAttenuation
119
+ depthWrite={false}
120
+ />
121
+ </points>
122
+ );
123
+ }