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.
- 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,416 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Data Bus
|
|
5
|
+
* Trace lines connecting the Cortex Core to memory banks
|
|
6
|
+
* Supports pulse animations for access events
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useMemo, useRef } from 'react';
|
|
10
|
+
import { useFrame } from '@react-three/fiber';
|
|
11
|
+
import * as THREE from 'three';
|
|
12
|
+
|
|
13
|
+
interface DataBusProps {
|
|
14
|
+
chipWidth?: number;
|
|
15
|
+
chipHeight?: number;
|
|
16
|
+
coreWidth?: number;
|
|
17
|
+
coreHeight?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface BusLineProps {
|
|
21
|
+
start: [number, number, number];
|
|
22
|
+
end: [number, number, number];
|
|
23
|
+
color?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Single bus trace line
|
|
27
|
+
function BusLine({ start, end, color = '#FFB347' }: BusLineProps) {
|
|
28
|
+
const geometry = useMemo(() => {
|
|
29
|
+
const points = [new THREE.Vector3(...start), new THREE.Vector3(...end)];
|
|
30
|
+
return new THREE.BufferGeometry().setFromPoints(points);
|
|
31
|
+
}, [start, end]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<line>
|
|
35
|
+
<primitive object={geometry} />
|
|
36
|
+
<lineBasicMaterial color={color} opacity={0.6} transparent linewidth={2} />
|
|
37
|
+
</line>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Branching bus traces (main trunk + branches)
|
|
42
|
+
function BusBranch({
|
|
43
|
+
trunk,
|
|
44
|
+
branches,
|
|
45
|
+
color = '#FFB347',
|
|
46
|
+
}: {
|
|
47
|
+
trunk: { start: [number, number, number]; end: [number, number, number] };
|
|
48
|
+
branches: { start: [number, number, number]; end: [number, number, number] }[];
|
|
49
|
+
color?: string;
|
|
50
|
+
}) {
|
|
51
|
+
return (
|
|
52
|
+
<group>
|
|
53
|
+
<BusLine start={trunk.start} end={trunk.end} color={color} />
|
|
54
|
+
{branches.map((branch, i) => (
|
|
55
|
+
<BusLine key={i} start={branch.start} end={branch.end} color={color} />
|
|
56
|
+
))}
|
|
57
|
+
</group>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function DataBus({
|
|
62
|
+
chipWidth = 16,
|
|
63
|
+
chipHeight = 12,
|
|
64
|
+
coreWidth = 2,
|
|
65
|
+
coreHeight = 1.2,
|
|
66
|
+
}: DataBusProps) {
|
|
67
|
+
const sectionHeight = chipHeight / 3;
|
|
68
|
+
const halfChipHeight = chipHeight / 2;
|
|
69
|
+
const halfCoreHeight = coreHeight / 2;
|
|
70
|
+
const halfCoreWidth = coreWidth / 2;
|
|
71
|
+
|
|
72
|
+
// Z position for all traces (slightly above substrate)
|
|
73
|
+
const z = 0.05;
|
|
74
|
+
|
|
75
|
+
// Generate bus traces
|
|
76
|
+
const busTraces = useMemo(() => {
|
|
77
|
+
// Main vertical trunk to STM (top)
|
|
78
|
+
const stmTrunk = {
|
|
79
|
+
start: [0, halfCoreHeight + 0.1, z] as [number, number, number],
|
|
80
|
+
end: [0, halfChipHeight - sectionHeight * 0.3, z] as [number, number, number],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Branches to STM grid columns
|
|
84
|
+
const stmBranches: { start: [number, number, number]; end: [number, number, number] }[] = [];
|
|
85
|
+
const branchY = halfChipHeight - sectionHeight * 0.5;
|
|
86
|
+
for (let i = -3; i <= 3; i++) {
|
|
87
|
+
if (i !== 0) {
|
|
88
|
+
const x = i * 1.5;
|
|
89
|
+
stmBranches.push({
|
|
90
|
+
start: [0, branchY, z],
|
|
91
|
+
end: [x, branchY, z],
|
|
92
|
+
});
|
|
93
|
+
// Vertical segment to grid
|
|
94
|
+
stmBranches.push({
|
|
95
|
+
start: [x, branchY, z],
|
|
96
|
+
end: [x, halfChipHeight - sectionHeight * 0.2, z],
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Main vertical trunk to LTM (bottom)
|
|
102
|
+
const ltmTrunk = {
|
|
103
|
+
start: [0, -halfCoreHeight - 0.1, z] as [number, number, number],
|
|
104
|
+
end: [0, -halfChipHeight + sectionHeight * 0.3, z] as [number, number, number],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Branches to LTM grid columns
|
|
108
|
+
const ltmBranches: { start: [number, number, number]; end: [number, number, number] }[] = [];
|
|
109
|
+
const ltmBranchY = -halfChipHeight + sectionHeight * 0.5;
|
|
110
|
+
for (let i = -3; i <= 3; i++) {
|
|
111
|
+
if (i !== 0) {
|
|
112
|
+
const x = i * 1.5;
|
|
113
|
+
ltmBranches.push({
|
|
114
|
+
start: [0, ltmBranchY, z],
|
|
115
|
+
end: [x, ltmBranchY, z],
|
|
116
|
+
});
|
|
117
|
+
// Vertical segment to grid
|
|
118
|
+
ltmBranches.push({
|
|
119
|
+
start: [x, ltmBranchY, z],
|
|
120
|
+
end: [x, -halfChipHeight + sectionHeight * 0.2, z],
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Horizontal traces to Episodic (left)
|
|
126
|
+
const episodicLeftTrunk = {
|
|
127
|
+
start: [-halfCoreWidth - 0.1, 0, z] as [number, number, number],
|
|
128
|
+
end: [-chipWidth / 2 + 1.5, 0, z] as [number, number, number],
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const episodicLeftBranches: { start: [number, number, number]; end: [number, number, number] }[] = [];
|
|
132
|
+
for (let i = 0; i < 3; i++) {
|
|
133
|
+
const x = -halfCoreWidth - 1 - i * 1.5;
|
|
134
|
+
episodicLeftBranches.push({
|
|
135
|
+
start: [x, 0, z],
|
|
136
|
+
end: [x, sectionHeight * 0.3, z],
|
|
137
|
+
});
|
|
138
|
+
episodicLeftBranches.push({
|
|
139
|
+
start: [x, 0, z],
|
|
140
|
+
end: [x, -sectionHeight * 0.3, z],
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Horizontal traces to Episodic (right)
|
|
145
|
+
const episodicRightTrunk = {
|
|
146
|
+
start: [halfCoreWidth + 0.1, 0, z] as [number, number, number],
|
|
147
|
+
end: [chipWidth / 2 - 1.5, 0, z] as [number, number, number],
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const episodicRightBranches: { start: [number, number, number]; end: [number, number, number] }[] = [];
|
|
151
|
+
for (let i = 0; i < 3; i++) {
|
|
152
|
+
const x = halfCoreWidth + 1 + i * 1.5;
|
|
153
|
+
episodicRightBranches.push({
|
|
154
|
+
start: [x, 0, z],
|
|
155
|
+
end: [x, sectionHeight * 0.3, z],
|
|
156
|
+
});
|
|
157
|
+
episodicRightBranches.push({
|
|
158
|
+
start: [x, 0, z],
|
|
159
|
+
end: [x, -sectionHeight * 0.3, z],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
stm: { trunk: stmTrunk, branches: stmBranches },
|
|
165
|
+
ltm: { trunk: ltmTrunk, branches: ltmBranches },
|
|
166
|
+
episodicLeft: { trunk: episodicLeftTrunk, branches: episodicLeftBranches },
|
|
167
|
+
episodicRight: { trunk: episodicRightTrunk, branches: episodicRightBranches },
|
|
168
|
+
};
|
|
169
|
+
}, [chipWidth, chipHeight, halfChipHeight, halfCoreHeight, halfCoreWidth, sectionHeight]);
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<group>
|
|
173
|
+
{/* STM bus (top) */}
|
|
174
|
+
<BusBranch trunk={busTraces.stm.trunk} branches={busTraces.stm.branches} color="#FFB347" />
|
|
175
|
+
|
|
176
|
+
{/* LTM bus (bottom) */}
|
|
177
|
+
<BusBranch trunk={busTraces.ltm.trunk} branches={busTraces.ltm.branches} color="#FFB347" />
|
|
178
|
+
|
|
179
|
+
{/* Episodic bus (left) */}
|
|
180
|
+
<BusBranch trunk={busTraces.episodicLeft.trunk} branches={busTraces.episodicLeft.branches} color="#FFB347" />
|
|
181
|
+
|
|
182
|
+
{/* Episodic bus (right) */}
|
|
183
|
+
<BusBranch trunk={busTraces.episodicRight.trunk} branches={busTraces.episodicRight.branches} color="#FFB347" />
|
|
184
|
+
|
|
185
|
+
{/* Junction nodes at branch points */}
|
|
186
|
+
<JunctionNodes chipWidth={chipWidth} chipHeight={chipHeight} coreWidth={coreWidth} />
|
|
187
|
+
</group>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Small glowing nodes at bus junctions
|
|
192
|
+
function JunctionNodes({
|
|
193
|
+
chipWidth,
|
|
194
|
+
chipHeight,
|
|
195
|
+
coreWidth,
|
|
196
|
+
}: {
|
|
197
|
+
chipWidth: number;
|
|
198
|
+
chipHeight: number;
|
|
199
|
+
coreWidth: number;
|
|
200
|
+
}) {
|
|
201
|
+
const nodesRef = useRef<THREE.InstancedMesh>(null);
|
|
202
|
+
const nodeGeometry = useMemo(() => new THREE.CircleGeometry(0.06, 8), []);
|
|
203
|
+
const nodeMaterial = useMemo(
|
|
204
|
+
() =>
|
|
205
|
+
new THREE.MeshBasicMaterial({
|
|
206
|
+
color: '#FFD700',
|
|
207
|
+
transparent: true,
|
|
208
|
+
opacity: 0.8,
|
|
209
|
+
}),
|
|
210
|
+
[]
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const positions = useMemo(() => {
|
|
214
|
+
const sectionHeight = chipHeight / 3;
|
|
215
|
+
const halfChipHeight = chipHeight / 2;
|
|
216
|
+
const halfCoreWidth = coreWidth / 2;
|
|
217
|
+
const z = 0.06;
|
|
218
|
+
|
|
219
|
+
const nodes: [number, number, number][] = [
|
|
220
|
+
// Core connection points
|
|
221
|
+
[0, halfCoreWidth * 0.6 + 0.1, z],
|
|
222
|
+
[0, -halfCoreWidth * 0.6 - 0.1, z],
|
|
223
|
+
[-halfCoreWidth - 0.1, 0, z],
|
|
224
|
+
[halfCoreWidth + 0.1, 0, z],
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
// STM branch junctions
|
|
228
|
+
const stmBranchY = halfChipHeight - sectionHeight * 0.5;
|
|
229
|
+
for (let i = -3; i <= 3; i++) {
|
|
230
|
+
if (i !== 0) {
|
|
231
|
+
nodes.push([i * 1.5, stmBranchY, z]);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// LTM branch junctions
|
|
236
|
+
const ltmBranchY = -halfChipHeight + sectionHeight * 0.5;
|
|
237
|
+
for (let i = -3; i <= 3; i++) {
|
|
238
|
+
if (i !== 0) {
|
|
239
|
+
nodes.push([i * 1.5, ltmBranchY, z]);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Episodic branch junctions
|
|
244
|
+
for (let i = 0; i < 3; i++) {
|
|
245
|
+
nodes.push([-halfCoreWidth - 1 - i * 1.5, 0, z]);
|
|
246
|
+
nodes.push([halfCoreWidth + 1 + i * 1.5, 0, z]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return nodes;
|
|
250
|
+
}, [chipHeight, coreWidth]);
|
|
251
|
+
|
|
252
|
+
// Animate junction glow
|
|
253
|
+
useFrame((state) => {
|
|
254
|
+
if (nodesRef.current) {
|
|
255
|
+
const material = nodesRef.current.material as THREE.MeshBasicMaterial;
|
|
256
|
+
const pulse = Math.sin(state.clock.elapsedTime * 3) * 0.5 + 0.5;
|
|
257
|
+
material.opacity = 0.5 + pulse * 0.3;
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<instancedMesh
|
|
263
|
+
ref={nodesRef}
|
|
264
|
+
args={[nodeGeometry, nodeMaterial, positions.length]}
|
|
265
|
+
>
|
|
266
|
+
{positions.map((pos, i) => {
|
|
267
|
+
const matrix = new THREE.Matrix4().setPosition(...pos);
|
|
268
|
+
nodesRef.current?.setMatrixAt(i, matrix);
|
|
269
|
+
return null;
|
|
270
|
+
})}
|
|
271
|
+
</instancedMesh>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Pulse that travels along the data bus
|
|
276
|
+
export interface AccessPulseData {
|
|
277
|
+
id: number;
|
|
278
|
+
memoryId: number;
|
|
279
|
+
section: 'stm' | 'episodic' | 'ltm';
|
|
280
|
+
gridPosition: [number, number]; // Column, row in grid
|
|
281
|
+
color: string;
|
|
282
|
+
startTime: number;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
interface DataBusPulseProps {
|
|
286
|
+
pulse: AccessPulseData;
|
|
287
|
+
chipWidth: number;
|
|
288
|
+
chipHeight: number;
|
|
289
|
+
onComplete: (id: number) => void;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function DataBusPulse({
|
|
293
|
+
pulse,
|
|
294
|
+
chipWidth,
|
|
295
|
+
chipHeight,
|
|
296
|
+
onComplete,
|
|
297
|
+
}: DataBusPulseProps) {
|
|
298
|
+
const pulseRef = useRef<THREE.Mesh>(null);
|
|
299
|
+
const trailRef = useRef<THREE.Mesh>(null);
|
|
300
|
+
const startTimeRef = useRef(pulse.startTime);
|
|
301
|
+
|
|
302
|
+
const sectionHeight = chipHeight / 3;
|
|
303
|
+
const halfChipHeight = chipHeight / 2;
|
|
304
|
+
|
|
305
|
+
// Calculate path based on section
|
|
306
|
+
const path = useMemo(() => {
|
|
307
|
+
const z = 0.1;
|
|
308
|
+
const [col] = pulse.gridPosition;
|
|
309
|
+
const targetX = (col - 3) * 1.5; // Map grid column to x position
|
|
310
|
+
|
|
311
|
+
let targetY: number;
|
|
312
|
+
let midY: number;
|
|
313
|
+
|
|
314
|
+
switch (pulse.section) {
|
|
315
|
+
case 'stm':
|
|
316
|
+
targetY = halfChipHeight - sectionHeight * 0.3;
|
|
317
|
+
midY = halfChipHeight - sectionHeight * 0.5;
|
|
318
|
+
return [
|
|
319
|
+
[0, 0, z], // Start at core
|
|
320
|
+
[0, midY, z], // Up the trunk
|
|
321
|
+
[targetX, midY, z], // Along branch
|
|
322
|
+
[targetX, targetY, z], // To memory
|
|
323
|
+
];
|
|
324
|
+
case 'ltm':
|
|
325
|
+
targetY = -halfChipHeight + sectionHeight * 0.3;
|
|
326
|
+
midY = -halfChipHeight + sectionHeight * 0.5;
|
|
327
|
+
return [
|
|
328
|
+
[0, 0, z],
|
|
329
|
+
[0, midY, z],
|
|
330
|
+
[targetX, midY, z],
|
|
331
|
+
[targetX, targetY, z],
|
|
332
|
+
];
|
|
333
|
+
case 'episodic':
|
|
334
|
+
default:
|
|
335
|
+
const side = col < 3 ? -1 : 1;
|
|
336
|
+
targetX;
|
|
337
|
+
return [
|
|
338
|
+
[0, 0, z],
|
|
339
|
+
[side * (Math.abs(targetX) + 1), 0, z],
|
|
340
|
+
[side * (Math.abs(targetX) + 1), pulse.gridPosition[1] * 0.8, z],
|
|
341
|
+
];
|
|
342
|
+
}
|
|
343
|
+
}, [pulse, halfChipHeight, sectionHeight]);
|
|
344
|
+
|
|
345
|
+
const duration = 500; // ms
|
|
346
|
+
|
|
347
|
+
useFrame(() => {
|
|
348
|
+
if (!pulseRef.current) return;
|
|
349
|
+
|
|
350
|
+
const elapsed = Date.now() - startTimeRef.current;
|
|
351
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
352
|
+
|
|
353
|
+
if (progress >= 1) {
|
|
354
|
+
onComplete(pulse.id);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Interpolate position along path
|
|
359
|
+
const totalSegments = path.length - 1;
|
|
360
|
+
const segmentProgress = progress * totalSegments;
|
|
361
|
+
const segmentIndex = Math.floor(segmentProgress);
|
|
362
|
+
const segmentT = segmentProgress - segmentIndex;
|
|
363
|
+
|
|
364
|
+
const currentIdx = Math.min(segmentIndex, totalSegments - 1);
|
|
365
|
+
const start = path[currentIdx];
|
|
366
|
+
const end = path[Math.min(currentIdx + 1, path.length - 1)];
|
|
367
|
+
|
|
368
|
+
pulseRef.current.position.set(
|
|
369
|
+
start[0] + (end[0] - start[0]) * segmentT,
|
|
370
|
+
start[1] + (end[1] - start[1]) * segmentT,
|
|
371
|
+
start[2] + (end[2] - start[2]) * segmentT
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
// Scale down as it travels
|
|
375
|
+
const scale = 1 - progress * 0.3;
|
|
376
|
+
pulseRef.current.scale.setScalar(scale);
|
|
377
|
+
|
|
378
|
+
// Trail follows slightly behind
|
|
379
|
+
if (trailRef.current && progress > 0.1) {
|
|
380
|
+
const trailProgress = Math.max(0, progress - 0.1);
|
|
381
|
+
const trailSegmentProgress = trailProgress * totalSegments;
|
|
382
|
+
const trailSegmentIndex = Math.floor(trailSegmentProgress);
|
|
383
|
+
const trailSegmentT = trailSegmentProgress - trailSegmentIndex;
|
|
384
|
+
|
|
385
|
+
const trailIdx = Math.min(trailSegmentIndex, totalSegments - 1);
|
|
386
|
+
const trailStart = path[trailIdx];
|
|
387
|
+
const trailEnd = path[Math.min(trailIdx + 1, path.length - 1)];
|
|
388
|
+
|
|
389
|
+
trailRef.current.position.set(
|
|
390
|
+
trailStart[0] + (trailEnd[0] - trailStart[0]) * trailSegmentT,
|
|
391
|
+
trailStart[1] + (trailEnd[1] - trailStart[1]) * trailSegmentT,
|
|
392
|
+
trailStart[2] + (trailEnd[2] - trailStart[2]) * trailSegmentT
|
|
393
|
+
);
|
|
394
|
+
trailRef.current.scale.setScalar(scale * 0.6);
|
|
395
|
+
|
|
396
|
+
const trailMaterial = trailRef.current.material as THREE.MeshBasicMaterial;
|
|
397
|
+
trailMaterial.opacity = 0.4 * (1 - progress);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
return (
|
|
402
|
+
<group>
|
|
403
|
+
{/* Main pulse */}
|
|
404
|
+
<mesh ref={pulseRef} position={[0, 0, 0.1]}>
|
|
405
|
+
<circleGeometry args={[0.12, 16]} />
|
|
406
|
+
<meshBasicMaterial color={pulse.color} transparent opacity={0.9} />
|
|
407
|
+
</mesh>
|
|
408
|
+
|
|
409
|
+
{/* Trail */}
|
|
410
|
+
<mesh ref={trailRef} position={[0, 0, 0.1]}>
|
|
411
|
+
<circleGeometry args={[0.08, 12]} />
|
|
412
|
+
<meshBasicMaterial color={pulse.color} transparent opacity={0.4} />
|
|
413
|
+
</mesh>
|
|
414
|
+
</group>
|
|
415
|
+
);
|
|
416
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Memory Cell
|
|
5
|
+
* Standard rectangular memory node for the chip grid
|
|
6
|
+
* Color-coded by category with glow based on salience
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useRef, useMemo, useState, useCallback } from 'react';
|
|
10
|
+
import { useFrame, ThreeEvent } from '@react-three/fiber';
|
|
11
|
+
import { Html } from '@react-three/drei';
|
|
12
|
+
import * as THREE from 'three';
|
|
13
|
+
import { Memory, MemoryCategory } from '@/types/memory';
|
|
14
|
+
|
|
15
|
+
// Category color mapping
|
|
16
|
+
const CATEGORY_COLORS: Record<MemoryCategory, string> = {
|
|
17
|
+
architecture: '#06b6d4', // cyan
|
|
18
|
+
pattern: '#22c55e', // green
|
|
19
|
+
preference: '#eab308', // yellow
|
|
20
|
+
error: '#ef4444', // red
|
|
21
|
+
context: '#f97316', // orange
|
|
22
|
+
learning: '#84cc16', // lime
|
|
23
|
+
todo: '#a855f7', // purple
|
|
24
|
+
note: '#3b82f6', // blue
|
|
25
|
+
relationship: '#6366f1', // indigo
|
|
26
|
+
custom: '#ec4899', // pink
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
interface MemoryCellProps {
|
|
30
|
+
memory: Memory;
|
|
31
|
+
position: [number, number, number];
|
|
32
|
+
onSelect?: (memory: Memory | null) => void;
|
|
33
|
+
isSelected?: boolean;
|
|
34
|
+
size?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Shared geometry for performance
|
|
38
|
+
const CELL_GEOMETRY = new THREE.BoxGeometry(0.8, 0.5, 0.15);
|
|
39
|
+
|
|
40
|
+
export function MemoryCell({
|
|
41
|
+
memory,
|
|
42
|
+
position,
|
|
43
|
+
onSelect,
|
|
44
|
+
isSelected = false,
|
|
45
|
+
size = 1,
|
|
46
|
+
}: MemoryCellProps) {
|
|
47
|
+
const meshRef = useRef<THREE.Mesh>(null);
|
|
48
|
+
const glowRef = useRef<THREE.Mesh>(null);
|
|
49
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
50
|
+
|
|
51
|
+
const color = CATEGORY_COLORS[memory.category] || CATEGORY_COLORS.custom;
|
|
52
|
+
|
|
53
|
+
// Main material
|
|
54
|
+
const material = useMemo(() => {
|
|
55
|
+
return new THREE.MeshStandardMaterial({
|
|
56
|
+
color: color,
|
|
57
|
+
emissive: color,
|
|
58
|
+
emissiveIntensity: 0.3 + memory.salience * 0.4,
|
|
59
|
+
metalness: 0.2,
|
|
60
|
+
roughness: 0.6,
|
|
61
|
+
});
|
|
62
|
+
}, [color, memory.salience]);
|
|
63
|
+
|
|
64
|
+
// Glow material
|
|
65
|
+
const glowMaterial = useMemo(() => {
|
|
66
|
+
return new THREE.MeshBasicMaterial({
|
|
67
|
+
color: color,
|
|
68
|
+
transparent: true,
|
|
69
|
+
opacity: 0.15 + memory.salience * 0.2,
|
|
70
|
+
side: THREE.BackSide,
|
|
71
|
+
});
|
|
72
|
+
}, [color, memory.salience]);
|
|
73
|
+
|
|
74
|
+
// Selection ring material
|
|
75
|
+
const selectionMaterial = useMemo(() => {
|
|
76
|
+
return new THREE.MeshBasicMaterial({
|
|
77
|
+
color: '#ffffff',
|
|
78
|
+
transparent: true,
|
|
79
|
+
opacity: 0.8,
|
|
80
|
+
side: THREE.DoubleSide,
|
|
81
|
+
});
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
// Animate pulse
|
|
85
|
+
useFrame((state) => {
|
|
86
|
+
if (!meshRef.current) return;
|
|
87
|
+
|
|
88
|
+
const time = state.clock.elapsedTime;
|
|
89
|
+
const mat = meshRef.current.material as THREE.MeshStandardMaterial;
|
|
90
|
+
|
|
91
|
+
// Base pulse based on salience (higher salience = faster pulse)
|
|
92
|
+
const pulseSpeed = 1 + memory.salience * 2;
|
|
93
|
+
const pulse = Math.sin(time * pulseSpeed) * 0.5 + 0.5;
|
|
94
|
+
|
|
95
|
+
// Adjust emissive intensity
|
|
96
|
+
mat.emissiveIntensity = 0.2 + memory.salience * 0.3 + pulse * 0.15;
|
|
97
|
+
|
|
98
|
+
// Hover effect
|
|
99
|
+
if (isHovered || isSelected) {
|
|
100
|
+
mat.emissiveIntensity += 0.3;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Glow pulse
|
|
104
|
+
if (glowRef.current) {
|
|
105
|
+
const glowMat = glowRef.current.material as THREE.MeshBasicMaterial;
|
|
106
|
+
glowMat.opacity = 0.1 + pulse * 0.1 + memory.salience * 0.15;
|
|
107
|
+
if (isHovered || isSelected) {
|
|
108
|
+
glowMat.opacity += 0.15;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const handleClick = useCallback(
|
|
114
|
+
(e: ThreeEvent<MouseEvent>) => {
|
|
115
|
+
e.stopPropagation();
|
|
116
|
+
onSelect?.(isSelected ? null : memory);
|
|
117
|
+
},
|
|
118
|
+
[memory, onSelect, isSelected]
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const handlePointerOver = useCallback((e: ThreeEvent<PointerEvent>) => {
|
|
122
|
+
e.stopPropagation();
|
|
123
|
+
setIsHovered(true);
|
|
124
|
+
document.body.style.cursor = 'pointer';
|
|
125
|
+
}, []);
|
|
126
|
+
|
|
127
|
+
const handlePointerOut = useCallback(() => {
|
|
128
|
+
setIsHovered(false);
|
|
129
|
+
document.body.style.cursor = 'auto';
|
|
130
|
+
}, []);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<group position={position} scale={size}>
|
|
134
|
+
{/* Main cell */}
|
|
135
|
+
<mesh
|
|
136
|
+
ref={meshRef}
|
|
137
|
+
geometry={CELL_GEOMETRY}
|
|
138
|
+
material={material}
|
|
139
|
+
onClick={handleClick}
|
|
140
|
+
onPointerOver={handlePointerOver}
|
|
141
|
+
onPointerOut={handlePointerOut}
|
|
142
|
+
/>
|
|
143
|
+
|
|
144
|
+
{/* Glow layer */}
|
|
145
|
+
<mesh ref={glowRef} scale={1.15}>
|
|
146
|
+
<boxGeometry args={[0.85, 0.55, 0.2]} />
|
|
147
|
+
<primitive object={glowMaterial} />
|
|
148
|
+
</mesh>
|
|
149
|
+
|
|
150
|
+
{/* Selection indicator */}
|
|
151
|
+
{isSelected && (
|
|
152
|
+
<mesh rotation={[Math.PI / 2, 0, 0]} position={[0, 0, 0.1]}>
|
|
153
|
+
<ringGeometry args={[0.5, 0.55, 4]} />
|
|
154
|
+
<primitive object={selectionMaterial} />
|
|
155
|
+
</mesh>
|
|
156
|
+
)}
|
|
157
|
+
|
|
158
|
+
{/* Tooltip on hover */}
|
|
159
|
+
{isHovered && (
|
|
160
|
+
<Html
|
|
161
|
+
position={[0, 0.5, 0]}
|
|
162
|
+
center
|
|
163
|
+
style={{
|
|
164
|
+
pointerEvents: 'none',
|
|
165
|
+
transform: 'translateY(-100%)',
|
|
166
|
+
}}
|
|
167
|
+
>
|
|
168
|
+
<div className="bg-slate-900/95 border border-slate-600 rounded-lg px-3 py-2 text-xs max-w-[200px] backdrop-blur-sm shadow-lg">
|
|
169
|
+
<div className="font-semibold text-white truncate">{memory.title}</div>
|
|
170
|
+
<div className="text-slate-400 mt-1 flex items-center gap-2">
|
|
171
|
+
<span
|
|
172
|
+
className="w-2 h-2 rounded-full"
|
|
173
|
+
style={{ backgroundColor: color }}
|
|
174
|
+
/>
|
|
175
|
+
<span className="capitalize">{memory.category}</span>
|
|
176
|
+
<span className="text-slate-500">|</span>
|
|
177
|
+
<span>{Math.round(memory.salience * 100)}%</span>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</Html>
|
|
181
|
+
)}
|
|
182
|
+
</group>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Flash effect when memory is accessed
|
|
187
|
+
export function MemoryCellFlash({
|
|
188
|
+
position,
|
|
189
|
+
color,
|
|
190
|
+
onComplete,
|
|
191
|
+
}: {
|
|
192
|
+
position: [number, number, number];
|
|
193
|
+
color: string;
|
|
194
|
+
onComplete: () => void;
|
|
195
|
+
}) {
|
|
196
|
+
const meshRef = useRef<THREE.Mesh>(null);
|
|
197
|
+
const startTime = useRef(Date.now());
|
|
198
|
+
|
|
199
|
+
useFrame(() => {
|
|
200
|
+
if (!meshRef.current) return;
|
|
201
|
+
|
|
202
|
+
const elapsed = Date.now() - startTime.current;
|
|
203
|
+
const duration = 400;
|
|
204
|
+
const progress = elapsed / duration;
|
|
205
|
+
|
|
206
|
+
if (progress >= 1) {
|
|
207
|
+
onComplete();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Expand and fade
|
|
212
|
+
const scale = 1 + progress * 0.5;
|
|
213
|
+
meshRef.current.scale.setScalar(scale);
|
|
214
|
+
|
|
215
|
+
const mat = meshRef.current.material as THREE.MeshBasicMaterial;
|
|
216
|
+
mat.opacity = 0.8 * (1 - progress);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<mesh ref={meshRef} position={position}>
|
|
221
|
+
<boxGeometry args={[0.9, 0.6, 0.2]} />
|
|
222
|
+
<meshBasicMaterial color={color} transparent opacity={0.8} />
|
|
223
|
+
</mesh>
|
|
224
|
+
);
|
|
225
|
+
}
|