claude-cortex 1.0.0 → 1.1.0
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.
- package/dashboard/README.md +36 -0
- package/dashboard/components.json +22 -0
- package/dashboard/eslint.config.mjs +18 -0
- package/dashboard/next.config.ts +7 -0
- package/dashboard/package-lock.json +7784 -0
- package/dashboard/package.json +42 -0
- package/dashboard/postcss.config.mjs +7 -0
- package/dashboard/public/file.svg +1 -0
- package/dashboard/public/globe.svg +1 -0
- package/dashboard/public/next.svg +1 -0
- package/dashboard/public/vercel.svg +1 -0
- package/dashboard/public/window.svg +1 -0
- package/dashboard/src/app/favicon.ico +0 -0
- package/dashboard/src/app/globals.css +125 -0
- package/dashboard/src/app/layout.tsx +35 -0
- package/dashboard/src/app/page.tsx +338 -0
- package/dashboard/src/components/Providers.tsx +27 -0
- package/dashboard/src/components/brain/ActivityPulseSystem.tsx +229 -0
- package/dashboard/src/components/brain/BrainMesh.tsx +118 -0
- package/dashboard/src/components/brain/BrainRegions.tsx +254 -0
- package/dashboard/src/components/brain/BrainScene.tsx +255 -0
- package/dashboard/src/components/brain/CategoryLabels.tsx +103 -0
- package/dashboard/src/components/brain/CoreSphere.tsx +215 -0
- package/dashboard/src/components/brain/DataFlowParticles.tsx +123 -0
- package/dashboard/src/components/brain/DataStreamRings.tsx +161 -0
- package/dashboard/src/components/brain/ElectronFlow.tsx +323 -0
- package/dashboard/src/components/brain/HolographicGrid.tsx +235 -0
- package/dashboard/src/components/brain/MemoryLinks.tsx +271 -0
- package/dashboard/src/components/brain/MemoryNode.tsx +245 -0
- package/dashboard/src/components/brain/NeuralPathways.tsx +441 -0
- package/dashboard/src/components/brain/SynapseNodes.tsx +306 -0
- package/dashboard/src/components/brain/TimelineControls.tsx +205 -0
- package/dashboard/src/components/chip/ChipScene.tsx +497 -0
- package/dashboard/src/components/chip/ChipSubstrate.tsx +238 -0
- package/dashboard/src/components/chip/CortexCore.tsx +210 -0
- package/dashboard/src/components/chip/DataBus.tsx +416 -0
- package/dashboard/src/components/chip/MemoryCell.tsx +225 -0
- package/dashboard/src/components/chip/MemoryGrid.tsx +328 -0
- package/dashboard/src/components/chip/QuantumCell.tsx +316 -0
- package/dashboard/src/components/chip/SectionLabel.tsx +113 -0
- package/dashboard/src/components/chip/index.ts +14 -0
- package/dashboard/src/components/controls/ControlPanel.tsx +100 -0
- package/dashboard/src/components/dashboard/StatsPanel.tsx +164 -0
- package/dashboard/src/components/debug/ActivityLog.tsx +238 -0
- package/dashboard/src/components/debug/DebugPanel.tsx +101 -0
- package/dashboard/src/components/debug/QueryTester.tsx +192 -0
- package/dashboard/src/components/debug/RelationshipGraph.tsx +403 -0
- package/dashboard/src/components/debug/SqlConsole.tsx +313 -0
- package/dashboard/src/components/memory/MemoryDetail.tsx +325 -0
- package/dashboard/src/components/ui/button.tsx +62 -0
- package/dashboard/src/components/ui/card.tsx +92 -0
- package/dashboard/src/components/ui/input.tsx +21 -0
- package/dashboard/src/hooks/useDebouncedValue.ts +24 -0
- package/dashboard/src/hooks/useMemories.ts +276 -0
- package/dashboard/src/hooks/useSuggestions.ts +46 -0
- package/dashboard/src/lib/category-colors.ts +84 -0
- package/dashboard/src/lib/position-algorithm.ts +177 -0
- package/dashboard/src/lib/simplex-noise.ts +217 -0
- package/dashboard/src/lib/store.ts +88 -0
- package/dashboard/src/lib/utils.ts +6 -0
- package/dashboard/src/lib/websocket.ts +216 -0
- package/dashboard/src/types/memory.ts +73 -0
- package/dashboard/tsconfig.json +34 -0
- package/dist/api/control.d.ts +27 -0
- package/dist/api/control.d.ts.map +1 -0
- package/dist/api/control.js +60 -0
- package/dist/api/control.js.map +1 -0
- package/dist/api/visualization-server.d.ts.map +1 -1
- package/dist/api/visualization-server.js +109 -2
- package/dist/api/visualization-server.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +80 -4
- package/dist/index.js.map +1 -1
- package/dist/memory/store.d.ts +6 -0
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js +14 -0
- package/dist/memory/store.js.map +1 -1
- package/package.json +7 -3
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Data Stream Rings
|
|
5
|
+
* Creates orbital rings around the brain with data particles flowing along them
|
|
6
|
+
* Part of the Jarvis holographic aesthetic - particles flow around ring circumferences
|
|
7
|
+
* Uses BufferGeometry with Float32Array for GPU efficiency
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useMemo, useRef } from 'react';
|
|
11
|
+
import { useFrame } from '@react-three/fiber';
|
|
12
|
+
import * as THREE from 'three';
|
|
13
|
+
|
|
14
|
+
interface RingConfig {
|
|
15
|
+
radius: number;
|
|
16
|
+
tiltX: number;
|
|
17
|
+
tiltZ: number;
|
|
18
|
+
particleCount: number;
|
|
19
|
+
speed: number;
|
|
20
|
+
colorIndex: number; // 0 = gold, 1 = warm gold, 2 = deep orange
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Ring configurations with different radii and tilts for visual variety
|
|
24
|
+
const RING_CONFIGS: RingConfig[] = [
|
|
25
|
+
{ radius: 4.0, tiltX: 0.3, tiltZ: 0.1, particleCount: 16, speed: 0.8, colorIndex: 0 },
|
|
26
|
+
{ radius: 4.5, tiltX: -0.2, tiltZ: 0.4, particleCount: 18, speed: 0.6, colorIndex: 1 },
|
|
27
|
+
{ radius: 5.0, tiltX: 0.5, tiltZ: -0.2, particleCount: 20, speed: 0.7, colorIndex: 2 },
|
|
28
|
+
{ radius: 5.5, tiltX: -0.4, tiltZ: -0.3, particleCount: 14, speed: 0.9, colorIndex: 0 },
|
|
29
|
+
{ radius: 6.0, tiltX: 0.1, tiltZ: 0.5, particleCount: 12, speed: 0.5, colorIndex: 1 },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// Golden color palette matching the Jarvis aesthetic
|
|
33
|
+
const COLORS = [
|
|
34
|
+
{ r: 1.0, g: 0.843, b: 0.0 }, // #FFD700 - Bright gold
|
|
35
|
+
{ r: 1.0, g: 0.702, b: 0.278 }, // #FFB347 - Warm gold
|
|
36
|
+
{ r: 1.0, g: 0.549, b: 0.0 }, // #FF8C00 - Deep orange
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
interface DataStreamRingProps {
|
|
40
|
+
config: RingConfig;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Single orbital ring with flowing particles
|
|
45
|
+
*/
|
|
46
|
+
function DataStreamRing({ config }: DataStreamRingProps) {
|
|
47
|
+
const pointsRef = useRef<THREE.Points>(null);
|
|
48
|
+
const groupRef = useRef<THREE.Group>(null);
|
|
49
|
+
|
|
50
|
+
// Initialize particle positions and phases along the ring circumference
|
|
51
|
+
const { positions, phases, colors } = useMemo(() => {
|
|
52
|
+
const { radius, particleCount, colorIndex } = config;
|
|
53
|
+
const positions = new Float32Array(particleCount * 3);
|
|
54
|
+
const phases = new Float32Array(particleCount);
|
|
55
|
+
const colors = new Float32Array(particleCount * 3);
|
|
56
|
+
|
|
57
|
+
const baseColor = COLORS[colorIndex];
|
|
58
|
+
|
|
59
|
+
for (let i = 0; i < particleCount; i++) {
|
|
60
|
+
// Distribute particles evenly around the ring with some randomness
|
|
61
|
+
const baseAngle = (i / particleCount) * Math.PI * 2;
|
|
62
|
+
const angleOffset = (Math.random() - 0.5) * 0.2; // Small random offset
|
|
63
|
+
const angle = baseAngle + angleOffset;
|
|
64
|
+
|
|
65
|
+
phases[i] = angle;
|
|
66
|
+
|
|
67
|
+
// Position on ring circumference (XZ plane, will be tilted by group rotation)
|
|
68
|
+
positions[i * 3] = Math.cos(angle) * radius;
|
|
69
|
+
positions[i * 3 + 1] = (Math.random() - 0.5) * 0.1; // Slight Y variation
|
|
70
|
+
positions[i * 3 + 2] = Math.sin(angle) * radius;
|
|
71
|
+
|
|
72
|
+
// Color with slight variation for visual interest
|
|
73
|
+
const colorVariation = 0.9 + Math.random() * 0.2;
|
|
74
|
+
colors[i * 3] = baseColor.r * colorVariation;
|
|
75
|
+
colors[i * 3 + 1] = baseColor.g * colorVariation;
|
|
76
|
+
colors[i * 3 + 2] = baseColor.b * colorVariation;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { positions, phases, colors };
|
|
80
|
+
}, [config]);
|
|
81
|
+
|
|
82
|
+
// Animate particles flowing around the ring
|
|
83
|
+
useFrame((state, delta) => {
|
|
84
|
+
if (!pointsRef.current) return;
|
|
85
|
+
|
|
86
|
+
const { radius, particleCount, speed } = config;
|
|
87
|
+
const positionAttr = pointsRef.current.geometry.attributes
|
|
88
|
+
.position as THREE.BufferAttribute;
|
|
89
|
+
const positions = positionAttr.array as Float32Array;
|
|
90
|
+
const time = state.clock.elapsedTime;
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < particleCount; i++) {
|
|
93
|
+
// Update phase (angle) to create flow around the ring
|
|
94
|
+
phases[i] += delta * speed;
|
|
95
|
+
|
|
96
|
+
// Wrap angle
|
|
97
|
+
if (phases[i] > Math.PI * 2) {
|
|
98
|
+
phases[i] -= Math.PI * 2;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const angle = phases[i];
|
|
102
|
+
|
|
103
|
+
// Add subtle wave motion to the radius for organic feel
|
|
104
|
+
const radiusWave = radius + Math.sin(time * 2 + angle * 3) * 0.05;
|
|
105
|
+
|
|
106
|
+
// Update position on ring circumference
|
|
107
|
+
positions[i * 3] = Math.cos(angle) * radiusWave;
|
|
108
|
+
positions[i * 3 + 1] = Math.sin(time * 1.5 + angle) * 0.08; // Gentle vertical oscillation
|
|
109
|
+
positions[i * 3 + 2] = Math.sin(angle) * radiusWave;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
positionAttr.needsUpdate = true;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<group ref={groupRef} rotation={[config.tiltX, 0, config.tiltZ]}>
|
|
117
|
+
<points ref={pointsRef}>
|
|
118
|
+
<bufferGeometry>
|
|
119
|
+
<bufferAttribute
|
|
120
|
+
attach="attributes-position"
|
|
121
|
+
args={[positions, 3]}
|
|
122
|
+
/>
|
|
123
|
+
<bufferAttribute
|
|
124
|
+
attach="attributes-color"
|
|
125
|
+
args={[colors, 3]}
|
|
126
|
+
/>
|
|
127
|
+
</bufferGeometry>
|
|
128
|
+
<pointsMaterial
|
|
129
|
+
size={0.04}
|
|
130
|
+
vertexColors
|
|
131
|
+
transparent
|
|
132
|
+
opacity={0.4}
|
|
133
|
+
blending={THREE.AdditiveBlending}
|
|
134
|
+
depthWrite={false}
|
|
135
|
+
sizeAttenuation
|
|
136
|
+
/>
|
|
137
|
+
</points>
|
|
138
|
+
</group>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
interface DataStreamRingsProps {
|
|
143
|
+
visible?: boolean;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* All orbital data stream rings around the brain
|
|
148
|
+
*/
|
|
149
|
+
export function DataStreamRings({ visible = true }: DataStreamRingsProps) {
|
|
150
|
+
if (!visible) return null;
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<group>
|
|
154
|
+
{RING_CONFIGS.map((config, index) => (
|
|
155
|
+
<DataStreamRing key={index} config={config} />
|
|
156
|
+
))}
|
|
157
|
+
</group>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export default DataStreamRings;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Electron Flow
|
|
5
|
+
* GPU-efficient particle system showing electrical signals flowing through the brain
|
|
6
|
+
* Uses buffer attributes for performant animation of many particles
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useMemo, useRef } from 'react';
|
|
10
|
+
import { useFrame } from '@react-three/fiber';
|
|
11
|
+
import * as THREE from 'three';
|
|
12
|
+
|
|
13
|
+
interface ElectronFlowProps {
|
|
14
|
+
count?: number;
|
|
15
|
+
speed?: number;
|
|
16
|
+
color?: string;
|
|
17
|
+
size?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Main electron flow through the brain
|
|
22
|
+
* Particles flow from front (STM) to back (LTM) with some randomness
|
|
23
|
+
*/
|
|
24
|
+
export function ElectronFlow({
|
|
25
|
+
count = 250,
|
|
26
|
+
speed = 0.5,
|
|
27
|
+
color = '#FFD700',
|
|
28
|
+
size = 0.04,
|
|
29
|
+
}: ElectronFlowProps) {
|
|
30
|
+
const pointsRef = useRef<THREE.Points>(null);
|
|
31
|
+
|
|
32
|
+
// Initialize particle positions, velocities, and properties
|
|
33
|
+
const { positions, velocities, phases, colors } = useMemo(() => {
|
|
34
|
+
const positions = new Float32Array(count * 3);
|
|
35
|
+
const velocities = new Float32Array(count * 3);
|
|
36
|
+
const phases = new Float32Array(count);
|
|
37
|
+
const colors = new Float32Array(count * 3);
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < count; i++) {
|
|
40
|
+
// Random position within brain volume
|
|
41
|
+
const theta = Math.random() * Math.PI * 2;
|
|
42
|
+
const phi = Math.PI * 0.2 + Math.random() * Math.PI * 0.6;
|
|
43
|
+
const r = 1.5 + Math.random() * 2;
|
|
44
|
+
|
|
45
|
+
positions[i * 3] = r * Math.sin(phi) * Math.cos(theta);
|
|
46
|
+
positions[i * 3 + 1] = r * Math.cos(phi);
|
|
47
|
+
positions[i * 3 + 2] = -3 + Math.random() * 6;
|
|
48
|
+
|
|
49
|
+
// Velocity - mostly flowing backward (STM to LTM direction)
|
|
50
|
+
velocities[i * 3] = (Math.random() - 0.5) * 0.3;
|
|
51
|
+
velocities[i * 3 + 1] = (Math.random() - 0.5) * 0.2;
|
|
52
|
+
velocities[i * 3 + 2] = -0.5 - Math.random() * 0.5; // Backward flow
|
|
53
|
+
|
|
54
|
+
// Random phase for varied animation
|
|
55
|
+
phases[i] = Math.random() * Math.PI * 2;
|
|
56
|
+
|
|
57
|
+
// Color gradient based on Z position (front=bright gold, middle=warm gold, back=deep orange)
|
|
58
|
+
const normalizedZ = (positions[i * 3 + 2] + 3) / 6;
|
|
59
|
+
if (normalizedZ > 0.6) {
|
|
60
|
+
// Front - bright gold (#FFD700)
|
|
61
|
+
colors[i * 3] = 1.0;
|
|
62
|
+
colors[i * 3 + 1] = 0.843;
|
|
63
|
+
colors[i * 3 + 2] = 0.0;
|
|
64
|
+
} else if (normalizedZ > 0.4) {
|
|
65
|
+
// Middle - warm gold (#FFB347)
|
|
66
|
+
colors[i * 3] = 1.0;
|
|
67
|
+
colors[i * 3 + 1] = 0.702;
|
|
68
|
+
colors[i * 3 + 2] = 0.278;
|
|
69
|
+
} else {
|
|
70
|
+
// Back - deep orange (#FF8C00)
|
|
71
|
+
colors[i * 3] = 1.0;
|
|
72
|
+
colors[i * 3 + 1] = 0.549;
|
|
73
|
+
colors[i * 3 + 2] = 0.0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { positions, velocities, phases, colors };
|
|
78
|
+
}, [count]);
|
|
79
|
+
|
|
80
|
+
// Animate particles
|
|
81
|
+
useFrame((state, delta) => {
|
|
82
|
+
if (!pointsRef.current) return;
|
|
83
|
+
|
|
84
|
+
const positionAttr = pointsRef.current.geometry.attributes
|
|
85
|
+
.position as THREE.BufferAttribute;
|
|
86
|
+
const colorAttr = pointsRef.current.geometry.attributes
|
|
87
|
+
.color as THREE.BufferAttribute;
|
|
88
|
+
const positions = positionAttr.array as Float32Array;
|
|
89
|
+
const colors = colorAttr.array as Float32Array;
|
|
90
|
+
|
|
91
|
+
for (let i = 0; i < count; i++) {
|
|
92
|
+
// Update position based on velocity
|
|
93
|
+
positions[i * 3] += velocities[i * 3] * delta * speed;
|
|
94
|
+
positions[i * 3 + 1] += velocities[i * 3 + 1] * delta * speed;
|
|
95
|
+
positions[i * 3 + 2] += velocities[i * 3 + 2] * delta * speed;
|
|
96
|
+
|
|
97
|
+
// Add some wave motion
|
|
98
|
+
const phase = phases[i];
|
|
99
|
+
positions[i * 3] += Math.sin(state.clock.elapsedTime * 2 + phase) * 0.002;
|
|
100
|
+
positions[i * 3 + 1] += Math.cos(state.clock.elapsedTime * 1.5 + phase) * 0.002;
|
|
101
|
+
|
|
102
|
+
// Wrap around when reaching back of brain
|
|
103
|
+
if (positions[i * 3 + 2] < -3.5) {
|
|
104
|
+
positions[i * 3 + 2] = 3.5;
|
|
105
|
+
// Randomize X and Y on wrap
|
|
106
|
+
const theta = Math.random() * Math.PI * 2;
|
|
107
|
+
const r = 1 + Math.random() * 2;
|
|
108
|
+
positions[i * 3] = r * Math.cos(theta);
|
|
109
|
+
positions[i * 3 + 1] = (Math.random() - 0.5) * 2;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Update color based on new Z position (golden gradient)
|
|
113
|
+
const normalizedZ = (positions[i * 3 + 2] + 3.5) / 7;
|
|
114
|
+
if (normalizedZ > 0.6) {
|
|
115
|
+
// Front - bright gold (#FFD700)
|
|
116
|
+
colors[i * 3] = 1.0;
|
|
117
|
+
colors[i * 3 + 1] = 0.843;
|
|
118
|
+
colors[i * 3 + 2] = 0.0;
|
|
119
|
+
} else if (normalizedZ > 0.35) {
|
|
120
|
+
// Middle - warm gold (#FFB347)
|
|
121
|
+
colors[i * 3] = 1.0;
|
|
122
|
+
colors[i * 3 + 1] = 0.702;
|
|
123
|
+
colors[i * 3 + 2] = 0.278;
|
|
124
|
+
} else {
|
|
125
|
+
// Back - deep orange (#FF8C00)
|
|
126
|
+
colors[i * 3] = 1.0;
|
|
127
|
+
colors[i * 3 + 1] = 0.549;
|
|
128
|
+
colors[i * 3 + 2] = 0.0;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
positionAttr.needsUpdate = true;
|
|
133
|
+
colorAttr.needsUpdate = true;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<points ref={pointsRef}>
|
|
138
|
+
<bufferGeometry>
|
|
139
|
+
<bufferAttribute
|
|
140
|
+
attach="attributes-position"
|
|
141
|
+
args={[positions, 3]}
|
|
142
|
+
/>
|
|
143
|
+
<bufferAttribute
|
|
144
|
+
attach="attributes-color"
|
|
145
|
+
args={[colors, 3]}
|
|
146
|
+
/>
|
|
147
|
+
</bufferGeometry>
|
|
148
|
+
<pointsMaterial
|
|
149
|
+
size={size}
|
|
150
|
+
vertexColors
|
|
151
|
+
transparent
|
|
152
|
+
opacity={0.8}
|
|
153
|
+
blending={THREE.AdditiveBlending}
|
|
154
|
+
depthWrite={false}
|
|
155
|
+
sizeAttenuation
|
|
156
|
+
/>
|
|
157
|
+
</points>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Radial burst of electrons (for action potential visualization)
|
|
163
|
+
*/
|
|
164
|
+
interface ElectronBurstProps {
|
|
165
|
+
origin: [number, number, number];
|
|
166
|
+
count?: number;
|
|
167
|
+
duration?: number;
|
|
168
|
+
color?: string;
|
|
169
|
+
onComplete?: () => void;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function ElectronBurst({
|
|
173
|
+
origin,
|
|
174
|
+
count = 20,
|
|
175
|
+
duration = 1,
|
|
176
|
+
color = '#ffffff',
|
|
177
|
+
onComplete,
|
|
178
|
+
}: ElectronBurstProps) {
|
|
179
|
+
const pointsRef = useRef<THREE.Points>(null);
|
|
180
|
+
const progressRef = useRef(0);
|
|
181
|
+
|
|
182
|
+
const { positions, velocities } = useMemo(() => {
|
|
183
|
+
const positions = new Float32Array(count * 3);
|
|
184
|
+
const velocities = new Float32Array(count * 3);
|
|
185
|
+
|
|
186
|
+
for (let i = 0; i < count; i++) {
|
|
187
|
+
// Start at origin
|
|
188
|
+
positions[i * 3] = origin[0];
|
|
189
|
+
positions[i * 3 + 1] = origin[1];
|
|
190
|
+
positions[i * 3 + 2] = origin[2];
|
|
191
|
+
|
|
192
|
+
// Random outward velocity
|
|
193
|
+
const theta = Math.random() * Math.PI * 2;
|
|
194
|
+
const phi = Math.random() * Math.PI;
|
|
195
|
+
const speed = 1 + Math.random() * 2;
|
|
196
|
+
|
|
197
|
+
velocities[i * 3] = Math.sin(phi) * Math.cos(theta) * speed;
|
|
198
|
+
velocities[i * 3 + 1] = Math.cos(phi) * speed;
|
|
199
|
+
velocities[i * 3 + 2] = Math.sin(phi) * Math.sin(theta) * speed;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return { positions, velocities };
|
|
203
|
+
}, [origin, count]);
|
|
204
|
+
|
|
205
|
+
useFrame((_, delta) => {
|
|
206
|
+
if (!pointsRef.current) return;
|
|
207
|
+
|
|
208
|
+
progressRef.current += delta / duration;
|
|
209
|
+
|
|
210
|
+
if (progressRef.current >= 1) {
|
|
211
|
+
onComplete?.();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const positionAttr = pointsRef.current.geometry.attributes
|
|
216
|
+
.position as THREE.BufferAttribute;
|
|
217
|
+
const positions = positionAttr.array as Float32Array;
|
|
218
|
+
|
|
219
|
+
for (let i = 0; i < count; i++) {
|
|
220
|
+
positions[i * 3] += velocities[i * 3] * delta;
|
|
221
|
+
positions[i * 3 + 1] += velocities[i * 3 + 1] * delta;
|
|
222
|
+
positions[i * 3 + 2] += velocities[i * 3 + 2] * delta;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
positionAttr.needsUpdate = true;
|
|
226
|
+
|
|
227
|
+
// Fade out
|
|
228
|
+
(pointsRef.current.material as THREE.PointsMaterial).opacity =
|
|
229
|
+
0.9 * (1 - progressRef.current);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<points ref={pointsRef}>
|
|
234
|
+
<bufferGeometry>
|
|
235
|
+
<bufferAttribute
|
|
236
|
+
attach="attributes-position"
|
|
237
|
+
args={[positions, 3]}
|
|
238
|
+
/>
|
|
239
|
+
</bufferGeometry>
|
|
240
|
+
<pointsMaterial
|
|
241
|
+
size={0.06}
|
|
242
|
+
color={color}
|
|
243
|
+
transparent
|
|
244
|
+
opacity={0.9}
|
|
245
|
+
blending={THREE.AdditiveBlending}
|
|
246
|
+
depthWrite={false}
|
|
247
|
+
/>
|
|
248
|
+
</points>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Spiral flow around the brain (decorative)
|
|
254
|
+
*/
|
|
255
|
+
export function SpiralFlow({
|
|
256
|
+
count = 60,
|
|
257
|
+
radius = 4,
|
|
258
|
+
speed = 0.3,
|
|
259
|
+
color = '#FFB347',
|
|
260
|
+
}: {
|
|
261
|
+
count?: number;
|
|
262
|
+
radius?: number;
|
|
263
|
+
speed?: number;
|
|
264
|
+
color?: string;
|
|
265
|
+
}) {
|
|
266
|
+
const pointsRef = useRef<THREE.Points>(null);
|
|
267
|
+
|
|
268
|
+
const { positions, phases } = useMemo(() => {
|
|
269
|
+
const positions = new Float32Array(count * 3);
|
|
270
|
+
const phases = new Float32Array(count);
|
|
271
|
+
|
|
272
|
+
for (let i = 0; i < count; i++) {
|
|
273
|
+
const t = i / count;
|
|
274
|
+
phases[i] = t * Math.PI * 4; // Multiple loops
|
|
275
|
+
|
|
276
|
+
positions[i * 3] = Math.cos(phases[i]) * radius;
|
|
277
|
+
positions[i * 3 + 1] = (t - 0.5) * 6;
|
|
278
|
+
positions[i * 3 + 2] = Math.sin(phases[i]) * radius;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return { positions, phases };
|
|
282
|
+
}, [count, radius]);
|
|
283
|
+
|
|
284
|
+
useFrame((state) => {
|
|
285
|
+
if (!pointsRef.current) return;
|
|
286
|
+
|
|
287
|
+
const positionAttr = pointsRef.current.geometry.attributes
|
|
288
|
+
.position as THREE.BufferAttribute;
|
|
289
|
+
const positions = positionAttr.array as Float32Array;
|
|
290
|
+
const time = state.clock.elapsedTime * speed;
|
|
291
|
+
|
|
292
|
+
for (let i = 0; i < count; i++) {
|
|
293
|
+
const t = i / count;
|
|
294
|
+
const phase = phases[i] + time;
|
|
295
|
+
const r = radius + Math.sin(phase * 2) * 0.3;
|
|
296
|
+
|
|
297
|
+
positions[i * 3] = Math.cos(phase) * r;
|
|
298
|
+
positions[i * 3 + 1] = (t - 0.5) * 6 + Math.sin(time + t * Math.PI * 2) * 0.2;
|
|
299
|
+
positions[i * 3 + 2] = Math.sin(phase) * r;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
positionAttr.needsUpdate = true;
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
return (
|
|
306
|
+
<points ref={pointsRef}>
|
|
307
|
+
<bufferGeometry>
|
|
308
|
+
<bufferAttribute
|
|
309
|
+
attach="attributes-position"
|
|
310
|
+
args={[positions, 3]}
|
|
311
|
+
/>
|
|
312
|
+
</bufferGeometry>
|
|
313
|
+
<pointsMaterial
|
|
314
|
+
size={0.05}
|
|
315
|
+
color={color}
|
|
316
|
+
transparent
|
|
317
|
+
opacity={0.4}
|
|
318
|
+
blending={THREE.AdditiveBlending}
|
|
319
|
+
depthWrite={false}
|
|
320
|
+
/>
|
|
321
|
+
</points>
|
|
322
|
+
);
|
|
323
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Holographic Grid
|
|
5
|
+
* Geodesic wireframe sphere surrounding the brain
|
|
6
|
+
* Creates the iconic Jarvis-style holographic grid effect
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - IcosahedronGeometry with detail level 2 for geodesic look
|
|
10
|
+
* - EdgesGeometry for clean wireframe rendering
|
|
11
|
+
* - Subtle pulsing animation (opacity oscillation)
|
|
12
|
+
* - Scan line effect (bright band moving vertically through the grid)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useMemo, useRef, useEffect } from 'react';
|
|
16
|
+
import { useFrame } from '@react-three/fiber';
|
|
17
|
+
import * as THREE from 'three';
|
|
18
|
+
|
|
19
|
+
interface HolographicGridProps {
|
|
20
|
+
/** Radius of the geodesic sphere (default: 6) */
|
|
21
|
+
radius?: number;
|
|
22
|
+
/** Base color of the grid (default: #FFB347 - warm gold) */
|
|
23
|
+
color?: string;
|
|
24
|
+
/** Base opacity of the grid (default: 0.08) */
|
|
25
|
+
opacity?: number;
|
|
26
|
+
/** Enable pulsing animation (default: true) */
|
|
27
|
+
enablePulse?: boolean;
|
|
28
|
+
/** Enable scan line effect (default: true) */
|
|
29
|
+
enableScanLine?: boolean;
|
|
30
|
+
/** Speed of the scan line (default: 1) */
|
|
31
|
+
scanLineSpeed?: number;
|
|
32
|
+
/** Visibility toggle */
|
|
33
|
+
visible?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Custom shader material for the holographic grid with scan line effect
|
|
38
|
+
*/
|
|
39
|
+
function createGridShaderMaterial(color: THREE.Color, baseOpacity: number) {
|
|
40
|
+
return new THREE.ShaderMaterial({
|
|
41
|
+
uniforms: {
|
|
42
|
+
uColor: { value: color },
|
|
43
|
+
uBaseOpacity: { value: baseOpacity },
|
|
44
|
+
uPulseOpacity: { value: 0 },
|
|
45
|
+
uScanLineY: { value: 0 },
|
|
46
|
+
uScanLineWidth: { value: 0.3 },
|
|
47
|
+
uScanLineIntensity: { value: 0.15 },
|
|
48
|
+
uTime: { value: 0 },
|
|
49
|
+
},
|
|
50
|
+
vertexShader: `
|
|
51
|
+
varying vec3 vPosition;
|
|
52
|
+
|
|
53
|
+
void main() {
|
|
54
|
+
vPosition = position;
|
|
55
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
56
|
+
}
|
|
57
|
+
`,
|
|
58
|
+
fragmentShader: `
|
|
59
|
+
uniform vec3 uColor;
|
|
60
|
+
uniform float uBaseOpacity;
|
|
61
|
+
uniform float uPulseOpacity;
|
|
62
|
+
uniform float uScanLineY;
|
|
63
|
+
uniform float uScanLineWidth;
|
|
64
|
+
uniform float uScanLineIntensity;
|
|
65
|
+
uniform float uTime;
|
|
66
|
+
|
|
67
|
+
varying vec3 vPosition;
|
|
68
|
+
|
|
69
|
+
void main() {
|
|
70
|
+
// Base opacity with pulse
|
|
71
|
+
float opacity = uBaseOpacity + uPulseOpacity;
|
|
72
|
+
|
|
73
|
+
// Scan line effect - bright band at current Y position
|
|
74
|
+
float scanLineDist = abs(vPosition.y - uScanLineY);
|
|
75
|
+
float scanLineEffect = smoothstep(uScanLineWidth, 0.0, scanLineDist);
|
|
76
|
+
|
|
77
|
+
// Add scan line brightness
|
|
78
|
+
float finalOpacity = opacity + scanLineEffect * uScanLineIntensity;
|
|
79
|
+
|
|
80
|
+
// Slight color shift for scan line (more white/bright)
|
|
81
|
+
vec3 finalColor = mix(uColor, vec3(1.0, 0.95, 0.8), scanLineEffect * 0.5);
|
|
82
|
+
|
|
83
|
+
gl_FragColor = vec4(finalColor, finalOpacity);
|
|
84
|
+
}
|
|
85
|
+
`,
|
|
86
|
+
transparent: true,
|
|
87
|
+
depthWrite: false,
|
|
88
|
+
side: THREE.DoubleSide,
|
|
89
|
+
blending: THREE.AdditiveBlending,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function HolographicGrid({
|
|
94
|
+
radius = 6,
|
|
95
|
+
color = '#FFB347',
|
|
96
|
+
opacity = 0.04,
|
|
97
|
+
enablePulse = true,
|
|
98
|
+
enableScanLine = true,
|
|
99
|
+
scanLineSpeed = 1,
|
|
100
|
+
visible = true,
|
|
101
|
+
}: HolographicGridProps) {
|
|
102
|
+
const lineSegmentsRef = useRef<THREE.LineSegments>(null);
|
|
103
|
+
const materialRef = useRef<THREE.ShaderMaterial | null>(null);
|
|
104
|
+
|
|
105
|
+
// Create the geodesic geometry and edges
|
|
106
|
+
const { geometry, edgesGeometry } = useMemo(() => {
|
|
107
|
+
// IcosahedronGeometry with detail level 2 creates a nice geodesic sphere
|
|
108
|
+
const icosahedron = new THREE.IcosahedronGeometry(radius, 2);
|
|
109
|
+
// EdgesGeometry extracts the edges for wireframe rendering
|
|
110
|
+
const edges = new THREE.EdgesGeometry(icosahedron);
|
|
111
|
+
|
|
112
|
+
return { geometry: icosahedron, edgesGeometry: edges };
|
|
113
|
+
}, [radius]);
|
|
114
|
+
|
|
115
|
+
// Create shader material
|
|
116
|
+
const material = useMemo(() => {
|
|
117
|
+
const colorObj = new THREE.Color(color);
|
|
118
|
+
return createGridShaderMaterial(colorObj, opacity);
|
|
119
|
+
}, [color, opacity]);
|
|
120
|
+
|
|
121
|
+
// Sync materialRef with material (cannot update ref during render)
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
materialRef.current = material;
|
|
124
|
+
}, [material]);
|
|
125
|
+
|
|
126
|
+
// Cleanup on unmount
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
return () => {
|
|
129
|
+
geometry.dispose();
|
|
130
|
+
edgesGeometry.dispose();
|
|
131
|
+
material.dispose();
|
|
132
|
+
};
|
|
133
|
+
}, [geometry, edgesGeometry, material]);
|
|
134
|
+
|
|
135
|
+
// Animation loop
|
|
136
|
+
useFrame((state) => {
|
|
137
|
+
if (!materialRef.current || !visible) return;
|
|
138
|
+
|
|
139
|
+
const time = state.clock.elapsedTime;
|
|
140
|
+
const uniforms = materialRef.current.uniforms;
|
|
141
|
+
|
|
142
|
+
// Update time uniform
|
|
143
|
+
uniforms.uTime.value = time;
|
|
144
|
+
|
|
145
|
+
// Subtle pulsing animation - opacity varies slightly
|
|
146
|
+
if (enablePulse) {
|
|
147
|
+
// Slow breathing effect with slight random flicker
|
|
148
|
+
const breathe = Math.sin(time * 0.8) * 0.015;
|
|
149
|
+
const flicker = Math.random() > 0.97 ? 0.02 : 0;
|
|
150
|
+
uniforms.uPulseOpacity.value = breathe + flicker;
|
|
151
|
+
} else {
|
|
152
|
+
uniforms.uPulseOpacity.value = 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Scan line effect - bright band moving vertically
|
|
156
|
+
if (enableScanLine) {
|
|
157
|
+
// Scan line moves from bottom (-radius) to top (+radius) and wraps
|
|
158
|
+
const scanCycle = (time * scanLineSpeed * 0.3) % 1;
|
|
159
|
+
// Smooth ease in/out for the scan position
|
|
160
|
+
const eased = scanCycle < 0.5
|
|
161
|
+
? 2 * scanCycle * scanCycle
|
|
162
|
+
: 1 - Math.pow(-2 * scanCycle + 2, 2) / 2;
|
|
163
|
+
const scanY = (eased * 2 - 1) * radius;
|
|
164
|
+
uniforms.uScanLineY.value = scanY;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (!visible) return null;
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<lineSegments ref={lineSegmentsRef} geometry={edgesGeometry} material={material} />
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Alternative simpler version using LineBasicMaterial (no scan line effect)
|
|
177
|
+
* Use this if you don't need the scan line or want better performance
|
|
178
|
+
*/
|
|
179
|
+
export function HolographicGridSimple({
|
|
180
|
+
radius = 6,
|
|
181
|
+
color = '#FFB347',
|
|
182
|
+
opacity = 0.04,
|
|
183
|
+
enablePulse = true,
|
|
184
|
+
visible = true,
|
|
185
|
+
}: Omit<HolographicGridProps, 'enableScanLine' | 'scanLineSpeed'>) {
|
|
186
|
+
const lineSegmentsRef = useRef<THREE.LineSegments>(null);
|
|
187
|
+
|
|
188
|
+
// Create the geodesic geometry and edges
|
|
189
|
+
const { geometry, edgesGeometry } = useMemo(() => {
|
|
190
|
+
const icosahedron = new THREE.IcosahedronGeometry(radius, 2);
|
|
191
|
+
const edges = new THREE.EdgesGeometry(icosahedron);
|
|
192
|
+
return { geometry: icosahedron, edgesGeometry: edges };
|
|
193
|
+
}, [radius]);
|
|
194
|
+
|
|
195
|
+
// Create line material
|
|
196
|
+
const material = useMemo(() => {
|
|
197
|
+
return new THREE.LineBasicMaterial({
|
|
198
|
+
color: new THREE.Color(color),
|
|
199
|
+
transparent: true,
|
|
200
|
+
opacity: opacity,
|
|
201
|
+
depthWrite: false,
|
|
202
|
+
blending: THREE.AdditiveBlending,
|
|
203
|
+
});
|
|
204
|
+
}, [color, opacity]);
|
|
205
|
+
|
|
206
|
+
// Cleanup on unmount
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
return () => {
|
|
209
|
+
geometry.dispose();
|
|
210
|
+
edgesGeometry.dispose();
|
|
211
|
+
material.dispose();
|
|
212
|
+
};
|
|
213
|
+
}, [geometry, edgesGeometry, material]);
|
|
214
|
+
|
|
215
|
+
// Pulsing animation
|
|
216
|
+
useFrame((state) => {
|
|
217
|
+
if (!lineSegmentsRef.current || !visible || !enablePulse) return;
|
|
218
|
+
|
|
219
|
+
const time = state.clock.elapsedTime;
|
|
220
|
+
const mat = lineSegmentsRef.current.material as THREE.LineBasicMaterial;
|
|
221
|
+
|
|
222
|
+
// Subtle breathing effect
|
|
223
|
+
const breathe = Math.sin(time * 0.8) * 0.015;
|
|
224
|
+
const flicker = Math.random() > 0.97 ? 0.02 : 0;
|
|
225
|
+
mat.opacity = opacity + breathe + flicker;
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (!visible) return null;
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
<lineSegments ref={lineSegmentsRef} geometry={edgesGeometry} material={material} />
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export default HolographicGrid;
|