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,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
+ }