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,328 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Memory Grid
|
|
5
|
+
* Organizes memories in a grid layout within each memory bank section
|
|
6
|
+
* Automatically switches between MemoryCell and QuantumCell based on salience
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useMemo } from 'react';
|
|
10
|
+
import { Memory, MemoryType } from '@/types/memory';
|
|
11
|
+
import { MemoryCell } from './MemoryCell';
|
|
12
|
+
import { QuantumCell } from './QuantumCell';
|
|
13
|
+
|
|
14
|
+
// Salience threshold for quantum cells
|
|
15
|
+
const QUANTUM_THRESHOLD = 0.7;
|
|
16
|
+
|
|
17
|
+
interface GridConfig {
|
|
18
|
+
columns: number;
|
|
19
|
+
cellWidth: number;
|
|
20
|
+
cellHeight: number;
|
|
21
|
+
horizontalGap: number;
|
|
22
|
+
verticalGap: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface MemoryGridProps {
|
|
26
|
+
memories: Memory[];
|
|
27
|
+
memoryType: MemoryType;
|
|
28
|
+
chipWidth: number;
|
|
29
|
+
chipHeight: number;
|
|
30
|
+
selectedMemory: Memory | null;
|
|
31
|
+
onSelectMemory: (memory: Memory | null) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Calculate grid position for a memory at given index
|
|
35
|
+
function calculateGridPosition(
|
|
36
|
+
index: number,
|
|
37
|
+
config: GridConfig,
|
|
38
|
+
sectionOffset: { x: number; y: number; z: number },
|
|
39
|
+
sectionWidth: number
|
|
40
|
+
): [number, number, number] {
|
|
41
|
+
const { columns, cellWidth, horizontalGap, verticalGap } = config;
|
|
42
|
+
|
|
43
|
+
const col = index % columns;
|
|
44
|
+
const row = Math.floor(index / columns);
|
|
45
|
+
|
|
46
|
+
// Calculate total grid width
|
|
47
|
+
const gridWidth = columns * cellWidth + (columns - 1) * horizontalGap;
|
|
48
|
+
const startX = -gridWidth / 2 + cellWidth / 2;
|
|
49
|
+
|
|
50
|
+
const x = sectionOffset.x + startX + col * (cellWidth + horizontalGap);
|
|
51
|
+
const y = sectionOffset.y - row * (cellWidth * 0.6 + verticalGap);
|
|
52
|
+
const z = sectionOffset.z;
|
|
53
|
+
|
|
54
|
+
return [x, y, z];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function MemoryGrid({
|
|
58
|
+
memories,
|
|
59
|
+
memoryType,
|
|
60
|
+
chipWidth,
|
|
61
|
+
chipHeight,
|
|
62
|
+
selectedMemory,
|
|
63
|
+
onSelectMemory,
|
|
64
|
+
}: MemoryGridProps) {
|
|
65
|
+
const sectionHeight = chipHeight / 3;
|
|
66
|
+
const halfChipHeight = chipHeight / 2;
|
|
67
|
+
|
|
68
|
+
// Grid configuration
|
|
69
|
+
const gridConfig: GridConfig = useMemo(
|
|
70
|
+
() => ({
|
|
71
|
+
columns: 8,
|
|
72
|
+
cellWidth: 1.0,
|
|
73
|
+
cellHeight: 0.6,
|
|
74
|
+
horizontalGap: 0.4,
|
|
75
|
+
verticalGap: 0.3,
|
|
76
|
+
}),
|
|
77
|
+
[]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Section offset based on memory type
|
|
81
|
+
const sectionOffset = useMemo(() => {
|
|
82
|
+
switch (memoryType) {
|
|
83
|
+
case 'short_term':
|
|
84
|
+
return {
|
|
85
|
+
x: 0,
|
|
86
|
+
y: halfChipHeight - sectionHeight * 0.35,
|
|
87
|
+
z: 0.2,
|
|
88
|
+
};
|
|
89
|
+
case 'long_term':
|
|
90
|
+
return {
|
|
91
|
+
x: 0,
|
|
92
|
+
y: -halfChipHeight + sectionHeight * 0.65,
|
|
93
|
+
z: 0.2,
|
|
94
|
+
};
|
|
95
|
+
case 'episodic':
|
|
96
|
+
default:
|
|
97
|
+
return {
|
|
98
|
+
x: 0,
|
|
99
|
+
y: 0,
|
|
100
|
+
z: 0.2,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}, [memoryType, halfChipHeight, sectionHeight]);
|
|
104
|
+
|
|
105
|
+
// Filter memories by type
|
|
106
|
+
const filteredMemories = useMemo(() => {
|
|
107
|
+
return memories.filter((m) => m.type === memoryType);
|
|
108
|
+
}, [memories, memoryType]);
|
|
109
|
+
|
|
110
|
+
// Sort memories by salience (highest first) for better visual hierarchy
|
|
111
|
+
const sortedMemories = useMemo(() => {
|
|
112
|
+
return [...filteredMemories].sort((a, b) => b.salience - a.salience);
|
|
113
|
+
}, [filteredMemories]);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<group>
|
|
117
|
+
{sortedMemories.map((memory, index) => {
|
|
118
|
+
const position = calculateGridPosition(
|
|
119
|
+
index,
|
|
120
|
+
gridConfig,
|
|
121
|
+
sectionOffset,
|
|
122
|
+
chipWidth
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const isQuantum = memory.salience >= QUANTUM_THRESHOLD;
|
|
126
|
+
|
|
127
|
+
if (isQuantum) {
|
|
128
|
+
return (
|
|
129
|
+
<QuantumCell
|
|
130
|
+
key={memory.id}
|
|
131
|
+
memory={memory}
|
|
132
|
+
position={position}
|
|
133
|
+
onSelect={onSelectMemory}
|
|
134
|
+
isSelected={selectedMemory?.id === memory.id}
|
|
135
|
+
size={1.2}
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<MemoryCell
|
|
142
|
+
key={memory.id}
|
|
143
|
+
memory={memory}
|
|
144
|
+
position={position}
|
|
145
|
+
onSelect={onSelectMemory}
|
|
146
|
+
isSelected={selectedMemory?.id === memory.id}
|
|
147
|
+
/>
|
|
148
|
+
);
|
|
149
|
+
})}
|
|
150
|
+
</group>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Get grid position for a specific memory (used by pulse animations)
|
|
155
|
+
export function getMemoryGridPosition(
|
|
156
|
+
memory: Memory,
|
|
157
|
+
memories: Memory[],
|
|
158
|
+
chipWidth: number,
|
|
159
|
+
chipHeight: number
|
|
160
|
+
): [number, number, number] | null {
|
|
161
|
+
const sectionHeight = chipHeight / 3;
|
|
162
|
+
const halfChipHeight = chipHeight / 2;
|
|
163
|
+
|
|
164
|
+
const gridConfig: GridConfig = {
|
|
165
|
+
columns: 8,
|
|
166
|
+
cellWidth: 1.0,
|
|
167
|
+
cellHeight: 0.6,
|
|
168
|
+
horizontalGap: 0.4,
|
|
169
|
+
verticalGap: 0.3,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Get section offset
|
|
173
|
+
let sectionOffset: { x: number; y: number; z: number };
|
|
174
|
+
switch (memory.type) {
|
|
175
|
+
case 'short_term':
|
|
176
|
+
sectionOffset = {
|
|
177
|
+
x: 0,
|
|
178
|
+
y: halfChipHeight - sectionHeight * 0.35,
|
|
179
|
+
z: 0.2,
|
|
180
|
+
};
|
|
181
|
+
break;
|
|
182
|
+
case 'long_term':
|
|
183
|
+
sectionOffset = {
|
|
184
|
+
x: 0,
|
|
185
|
+
y: -halfChipHeight + sectionHeight * 0.65,
|
|
186
|
+
z: 0.2,
|
|
187
|
+
};
|
|
188
|
+
break;
|
|
189
|
+
case 'episodic':
|
|
190
|
+
default:
|
|
191
|
+
sectionOffset = {
|
|
192
|
+
x: 0,
|
|
193
|
+
y: 0,
|
|
194
|
+
z: 0.2,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Find index of this memory in its section (sorted by salience)
|
|
199
|
+
const sectionMemories = memories
|
|
200
|
+
.filter((m) => m.type === memory.type)
|
|
201
|
+
.sort((a, b) => b.salience - a.salience);
|
|
202
|
+
|
|
203
|
+
const index = sectionMemories.findIndex((m) => m.id === memory.id);
|
|
204
|
+
if (index === -1) return null;
|
|
205
|
+
|
|
206
|
+
return calculateGridPosition(index, gridConfig, sectionOffset, chipWidth);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Episodic memories need special handling since they're split left/right of core
|
|
210
|
+
export function EpisodicMemoryGrid({
|
|
211
|
+
memories,
|
|
212
|
+
chipWidth,
|
|
213
|
+
chipHeight,
|
|
214
|
+
coreWidth,
|
|
215
|
+
selectedMemory,
|
|
216
|
+
onSelectMemory,
|
|
217
|
+
}: {
|
|
218
|
+
memories: Memory[];
|
|
219
|
+
chipWidth: number;
|
|
220
|
+
chipHeight: number;
|
|
221
|
+
coreWidth: number;
|
|
222
|
+
selectedMemory: Memory | null;
|
|
223
|
+
onSelectMemory: (memory: Memory | null) => void;
|
|
224
|
+
}) {
|
|
225
|
+
const episodicMemories = useMemo(() => {
|
|
226
|
+
return memories
|
|
227
|
+
.filter((m) => m.type === 'episodic')
|
|
228
|
+
.sort((a, b) => b.salience - a.salience);
|
|
229
|
+
}, [memories]);
|
|
230
|
+
|
|
231
|
+
// Split memories between left and right of core
|
|
232
|
+
const leftMemories = episodicMemories.filter((_, i) => i % 2 === 0);
|
|
233
|
+
const rightMemories = episodicMemories.filter((_, i) => i % 2 === 1);
|
|
234
|
+
|
|
235
|
+
const gridConfig: GridConfig = {
|
|
236
|
+
columns: 3,
|
|
237
|
+
cellWidth: 1.0,
|
|
238
|
+
cellHeight: 0.6,
|
|
239
|
+
horizontalGap: 0.4,
|
|
240
|
+
verticalGap: 0.3,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const leftOffset = {
|
|
244
|
+
x: -coreWidth - 2,
|
|
245
|
+
y: 0.5,
|
|
246
|
+
z: 0.2,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const rightOffset = {
|
|
250
|
+
x: coreWidth + 2,
|
|
251
|
+
y: 0.5,
|
|
252
|
+
z: 0.2,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<group>
|
|
257
|
+
{/* Left side */}
|
|
258
|
+
{leftMemories.map((memory, index) => {
|
|
259
|
+
const position = calculateGridPosition(
|
|
260
|
+
index,
|
|
261
|
+
gridConfig,
|
|
262
|
+
leftOffset,
|
|
263
|
+
chipWidth / 2 - coreWidth
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
const isQuantum = memory.salience >= QUANTUM_THRESHOLD;
|
|
267
|
+
|
|
268
|
+
if (isQuantum) {
|
|
269
|
+
return (
|
|
270
|
+
<QuantumCell
|
|
271
|
+
key={memory.id}
|
|
272
|
+
memory={memory}
|
|
273
|
+
position={position}
|
|
274
|
+
onSelect={onSelectMemory}
|
|
275
|
+
isSelected={selectedMemory?.id === memory.id}
|
|
276
|
+
size={1.2}
|
|
277
|
+
/>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<MemoryCell
|
|
283
|
+
key={memory.id}
|
|
284
|
+
memory={memory}
|
|
285
|
+
position={position}
|
|
286
|
+
onSelect={onSelectMemory}
|
|
287
|
+
isSelected={selectedMemory?.id === memory.id}
|
|
288
|
+
/>
|
|
289
|
+
);
|
|
290
|
+
})}
|
|
291
|
+
|
|
292
|
+
{/* Right side */}
|
|
293
|
+
{rightMemories.map((memory, index) => {
|
|
294
|
+
const position = calculateGridPosition(
|
|
295
|
+
index,
|
|
296
|
+
gridConfig,
|
|
297
|
+
rightOffset,
|
|
298
|
+
chipWidth / 2 - coreWidth
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const isQuantum = memory.salience >= QUANTUM_THRESHOLD;
|
|
302
|
+
|
|
303
|
+
if (isQuantum) {
|
|
304
|
+
return (
|
|
305
|
+
<QuantumCell
|
|
306
|
+
key={memory.id}
|
|
307
|
+
memory={memory}
|
|
308
|
+
position={position}
|
|
309
|
+
onSelect={onSelectMemory}
|
|
310
|
+
isSelected={selectedMemory?.id === memory.id}
|
|
311
|
+
size={1.2}
|
|
312
|
+
/>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return (
|
|
317
|
+
<MemoryCell
|
|
318
|
+
key={memory.id}
|
|
319
|
+
memory={memory}
|
|
320
|
+
position={position}
|
|
321
|
+
onSelect={onSelectMemory}
|
|
322
|
+
isSelected={selectedMemory?.id === memory.id}
|
|
323
|
+
/>
|
|
324
|
+
);
|
|
325
|
+
})}
|
|
326
|
+
</group>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Quantum Cell
|
|
5
|
+
* High-salience memories rendered as rotating Bloch spheres
|
|
6
|
+
* Represents "quantum" memories with special visual treatment
|
|
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 (same as MemoryCell)
|
|
16
|
+
const CATEGORY_COLORS: Record<MemoryCategory, string> = {
|
|
17
|
+
architecture: '#06b6d4',
|
|
18
|
+
pattern: '#22c55e',
|
|
19
|
+
preference: '#eab308',
|
|
20
|
+
error: '#ef4444',
|
|
21
|
+
context: '#f97316',
|
|
22
|
+
learning: '#84cc16',
|
|
23
|
+
todo: '#a855f7',
|
|
24
|
+
note: '#3b82f6',
|
|
25
|
+
relationship: '#6366f1',
|
|
26
|
+
custom: '#ec4899',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Category angles for state vector direction (in radians)
|
|
30
|
+
const CATEGORY_ANGLES: Record<MemoryCategory, number> = {
|
|
31
|
+
architecture: 0,
|
|
32
|
+
pattern: Math.PI / 5,
|
|
33
|
+
preference: (2 * Math.PI) / 5,
|
|
34
|
+
error: (3 * Math.PI) / 5,
|
|
35
|
+
context: (4 * Math.PI) / 5,
|
|
36
|
+
learning: Math.PI,
|
|
37
|
+
todo: (6 * Math.PI) / 5,
|
|
38
|
+
note: (7 * Math.PI) / 5,
|
|
39
|
+
relationship: (8 * Math.PI) / 5,
|
|
40
|
+
custom: (9 * Math.PI) / 5,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
interface QuantumCellProps {
|
|
44
|
+
memory: Memory;
|
|
45
|
+
position: [number, number, number];
|
|
46
|
+
onSelect?: (memory: Memory | null) => void;
|
|
47
|
+
isSelected?: boolean;
|
|
48
|
+
size?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function QuantumCell({
|
|
52
|
+
memory,
|
|
53
|
+
position,
|
|
54
|
+
onSelect,
|
|
55
|
+
isSelected = false,
|
|
56
|
+
size = 1,
|
|
57
|
+
}: QuantumCellProps) {
|
|
58
|
+
const groupRef = useRef<THREE.Group>(null);
|
|
59
|
+
const sphereRef = useRef<THREE.LineSegments>(null);
|
|
60
|
+
const arrowRef = useRef<THREE.Group>(null);
|
|
61
|
+
const glowRef = useRef<THREE.Mesh>(null);
|
|
62
|
+
const interferenceRef = useRef<THREE.Points>(null);
|
|
63
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
64
|
+
|
|
65
|
+
const color = CATEGORY_COLORS[memory.category] || CATEGORY_COLORS.custom;
|
|
66
|
+
const stateAngle = CATEGORY_ANGLES[memory.category] || 0;
|
|
67
|
+
|
|
68
|
+
// Sphere wireframe geometry
|
|
69
|
+
const sphereGeometry = useMemo(() => {
|
|
70
|
+
const sphere = new THREE.SphereGeometry(0.4, 16, 12);
|
|
71
|
+
return new THREE.EdgesGeometry(sphere);
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
// Wireframe material
|
|
75
|
+
const wireMaterial = useMemo(() => {
|
|
76
|
+
return new THREE.LineBasicMaterial({
|
|
77
|
+
color: color,
|
|
78
|
+
transparent: true,
|
|
79
|
+
opacity: 0.6,
|
|
80
|
+
});
|
|
81
|
+
}, [color]);
|
|
82
|
+
|
|
83
|
+
// Glow sphere material
|
|
84
|
+
const glowMaterial = useMemo(() => {
|
|
85
|
+
return new THREE.MeshBasicMaterial({
|
|
86
|
+
color: color,
|
|
87
|
+
transparent: true,
|
|
88
|
+
opacity: 0.15,
|
|
89
|
+
side: THREE.BackSide,
|
|
90
|
+
});
|
|
91
|
+
}, [color]);
|
|
92
|
+
|
|
93
|
+
// Interference pattern particles
|
|
94
|
+
const interferenceGeometry = useMemo(() => {
|
|
95
|
+
const positions = new Float32Array(60 * 3);
|
|
96
|
+
for (let i = 0; i < 60; i++) {
|
|
97
|
+
const theta = Math.random() * Math.PI * 2;
|
|
98
|
+
const phi = Math.acos(2 * Math.random() - 1);
|
|
99
|
+
const r = 0.35 + Math.random() * 0.1;
|
|
100
|
+
positions[i * 3] = r * Math.sin(phi) * Math.cos(theta);
|
|
101
|
+
positions[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta);
|
|
102
|
+
positions[i * 3 + 2] = r * Math.cos(phi);
|
|
103
|
+
}
|
|
104
|
+
const geometry = new THREE.BufferGeometry();
|
|
105
|
+
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
|
106
|
+
return geometry;
|
|
107
|
+
}, []);
|
|
108
|
+
|
|
109
|
+
// Animate rotation and effects
|
|
110
|
+
useFrame((state) => {
|
|
111
|
+
if (!groupRef.current) return;
|
|
112
|
+
|
|
113
|
+
const time = state.clock.elapsedTime;
|
|
114
|
+
const rotationSpeed = 0.3 + memory.salience * 0.3;
|
|
115
|
+
|
|
116
|
+
// Continuous rotation
|
|
117
|
+
groupRef.current.rotation.y = time * rotationSpeed;
|
|
118
|
+
groupRef.current.rotation.x = Math.sin(time * 0.5) * 0.1;
|
|
119
|
+
|
|
120
|
+
// Hover/select: speed up rotation
|
|
121
|
+
if (isHovered || isSelected) {
|
|
122
|
+
groupRef.current.rotation.y = time * rotationSpeed * 2;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Wireframe pulse
|
|
126
|
+
if (sphereRef.current) {
|
|
127
|
+
const mat = sphereRef.current.material as THREE.LineBasicMaterial;
|
|
128
|
+
const pulse = Math.sin(time * 2) * 0.5 + 0.5;
|
|
129
|
+
mat.opacity = 0.4 + pulse * 0.3 + memory.salience * 0.2;
|
|
130
|
+
if (isHovered || isSelected) {
|
|
131
|
+
mat.opacity += 0.2;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Glow pulse
|
|
136
|
+
if (glowRef.current) {
|
|
137
|
+
const mat = glowRef.current.material as THREE.MeshBasicMaterial;
|
|
138
|
+
const pulse = Math.sin(time * 1.5) * 0.5 + 0.5;
|
|
139
|
+
mat.opacity = 0.1 + pulse * 0.15;
|
|
140
|
+
glowRef.current.scale.setScalar(1.2 + pulse * 0.1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Interference shimmer
|
|
144
|
+
if (interferenceRef.current) {
|
|
145
|
+
const positions = interferenceRef.current.geometry.attributes.position.array as Float32Array;
|
|
146
|
+
for (let i = 0; i < positions.length / 3; i++) {
|
|
147
|
+
const idx = i * 3;
|
|
148
|
+
const shimmer = Math.sin(time * 3 + i * 0.5) * 0.02;
|
|
149
|
+
// Just modify radius slightly for shimmer effect
|
|
150
|
+
const x = positions[idx];
|
|
151
|
+
const y = positions[idx + 1];
|
|
152
|
+
const z = positions[idx + 2];
|
|
153
|
+
const r = Math.sqrt(x * x + y * y + z * z);
|
|
154
|
+
const newR = 0.38 + shimmer;
|
|
155
|
+
const scale = newR / r;
|
|
156
|
+
positions[idx] = x * scale;
|
|
157
|
+
positions[idx + 1] = y * scale;
|
|
158
|
+
positions[idx + 2] = z * scale;
|
|
159
|
+
}
|
|
160
|
+
interferenceRef.current.geometry.attributes.position.needsUpdate = true;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const handleClick = useCallback(
|
|
165
|
+
(e: ThreeEvent<MouseEvent>) => {
|
|
166
|
+
e.stopPropagation();
|
|
167
|
+
onSelect?.(isSelected ? null : memory);
|
|
168
|
+
},
|
|
169
|
+
[memory, onSelect, isSelected]
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const handlePointerOver = useCallback((e: ThreeEvent<PointerEvent>) => {
|
|
173
|
+
e.stopPropagation();
|
|
174
|
+
setIsHovered(true);
|
|
175
|
+
document.body.style.cursor = 'pointer';
|
|
176
|
+
}, []);
|
|
177
|
+
|
|
178
|
+
const handlePointerOut = useCallback(() => {
|
|
179
|
+
setIsHovered(false);
|
|
180
|
+
document.body.style.cursor = 'auto';
|
|
181
|
+
}, []);
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<group position={position} scale={size}>
|
|
185
|
+
<group
|
|
186
|
+
ref={groupRef}
|
|
187
|
+
onClick={handleClick}
|
|
188
|
+
onPointerOver={handlePointerOver}
|
|
189
|
+
onPointerOut={handlePointerOut}
|
|
190
|
+
>
|
|
191
|
+
{/* Wireframe Bloch sphere */}
|
|
192
|
+
<lineSegments ref={sphereRef} geometry={sphereGeometry} material={wireMaterial} />
|
|
193
|
+
|
|
194
|
+
{/* Axis lines */}
|
|
195
|
+
<AxisLines color={color} />
|
|
196
|
+
|
|
197
|
+
{/* State vector arrow */}
|
|
198
|
+
<group ref={arrowRef} rotation={[0, stateAngle, Math.PI / 4]}>
|
|
199
|
+
<StateVector color={color} />
|
|
200
|
+
</group>
|
|
201
|
+
|
|
202
|
+
{/* Glow sphere */}
|
|
203
|
+
<mesh ref={glowRef}>
|
|
204
|
+
<sphereGeometry args={[0.5, 12, 8]} />
|
|
205
|
+
<primitive object={glowMaterial} />
|
|
206
|
+
</mesh>
|
|
207
|
+
|
|
208
|
+
{/* Interference pattern particles */}
|
|
209
|
+
<points ref={interferenceRef} geometry={interferenceGeometry}>
|
|
210
|
+
<pointsMaterial
|
|
211
|
+
color={color}
|
|
212
|
+
size={0.03}
|
|
213
|
+
transparent
|
|
214
|
+
opacity={0.4}
|
|
215
|
+
sizeAttenuation
|
|
216
|
+
/>
|
|
217
|
+
</points>
|
|
218
|
+
|
|
219
|
+
{/* Clickable invisible sphere */}
|
|
220
|
+
<mesh visible={false}>
|
|
221
|
+
<sphereGeometry args={[0.5, 8, 6]} />
|
|
222
|
+
<meshBasicMaterial transparent opacity={0} />
|
|
223
|
+
</mesh>
|
|
224
|
+
</group>
|
|
225
|
+
|
|
226
|
+
{/* Selection ring */}
|
|
227
|
+
{isSelected && (
|
|
228
|
+
<mesh rotation={[Math.PI / 2, 0, 0]} position={[0, 0, 0]}>
|
|
229
|
+
<ringGeometry args={[0.55, 0.6, 32]} />
|
|
230
|
+
<meshBasicMaterial color="#ffffff" transparent opacity={0.7} side={THREE.DoubleSide} />
|
|
231
|
+
</mesh>
|
|
232
|
+
)}
|
|
233
|
+
|
|
234
|
+
{/* Quantum label */}
|
|
235
|
+
<Html
|
|
236
|
+
position={[0, -0.6, 0]}
|
|
237
|
+
center
|
|
238
|
+
style={{ pointerEvents: 'none' }}
|
|
239
|
+
>
|
|
240
|
+
<div className="text-[8px] font-mono text-amber-400/60 whitespace-nowrap">
|
|
241
|
+
|Q{memory.id}⟩
|
|
242
|
+
</div>
|
|
243
|
+
</Html>
|
|
244
|
+
|
|
245
|
+
{/* Tooltip on hover */}
|
|
246
|
+
{isHovered && (
|
|
247
|
+
<Html
|
|
248
|
+
position={[0, 0.7, 0]}
|
|
249
|
+
center
|
|
250
|
+
style={{
|
|
251
|
+
pointerEvents: 'none',
|
|
252
|
+
transform: 'translateY(-100%)',
|
|
253
|
+
}}
|
|
254
|
+
>
|
|
255
|
+
<div className="bg-slate-900/95 border border-amber-600/50 rounded-lg px-3 py-2 text-xs max-w-[220px] backdrop-blur-sm shadow-lg">
|
|
256
|
+
<div className="flex items-center gap-2 mb-1">
|
|
257
|
+
<span className="text-amber-400 text-[10px]">QUANTUM</span>
|
|
258
|
+
<span className="text-slate-500">|</span>
|
|
259
|
+
<span className="text-slate-400">{Math.round(memory.salience * 100)}% salience</span>
|
|
260
|
+
</div>
|
|
261
|
+
<div className="font-semibold text-white truncate">{memory.title}</div>
|
|
262
|
+
<div className="text-slate-400 mt-1 flex items-center gap-2">
|
|
263
|
+
<span
|
|
264
|
+
className="w-2 h-2 rounded-full"
|
|
265
|
+
style={{ backgroundColor: color }}
|
|
266
|
+
/>
|
|
267
|
+
<span className="capitalize">{memory.category}</span>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
</Html>
|
|
271
|
+
)}
|
|
272
|
+
</group>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Axis lines (x, y, z) inside the Bloch sphere
|
|
277
|
+
function AxisLines({ color }: { color: string }) {
|
|
278
|
+
const geometry = useMemo(() => {
|
|
279
|
+
const points = [
|
|
280
|
+
// X axis
|
|
281
|
+
new THREE.Vector3(-0.35, 0, 0),
|
|
282
|
+
new THREE.Vector3(0.35, 0, 0),
|
|
283
|
+
// Y axis
|
|
284
|
+
new THREE.Vector3(0, -0.35, 0),
|
|
285
|
+
new THREE.Vector3(0, 0.35, 0),
|
|
286
|
+
// Z axis
|
|
287
|
+
new THREE.Vector3(0, 0, -0.35),
|
|
288
|
+
new THREE.Vector3(0, 0, 0.35),
|
|
289
|
+
];
|
|
290
|
+
return new THREE.BufferGeometry().setFromPoints(points);
|
|
291
|
+
}, []);
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
<lineSegments geometry={geometry}>
|
|
295
|
+
<lineBasicMaterial color={color} transparent opacity={0.3} />
|
|
296
|
+
</lineSegments>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// State vector arrow pointing in a direction
|
|
301
|
+
function StateVector({ color }: { color: string }) {
|
|
302
|
+
return (
|
|
303
|
+
<group>
|
|
304
|
+
{/* Arrow shaft */}
|
|
305
|
+
<mesh position={[0, 0.15, 0]}>
|
|
306
|
+
<cylinderGeometry args={[0.02, 0.02, 0.3, 8]} />
|
|
307
|
+
<meshBasicMaterial color={color} />
|
|
308
|
+
</mesh>
|
|
309
|
+
{/* Arrow head */}
|
|
310
|
+
<mesh position={[0, 0.32, 0]}>
|
|
311
|
+
<coneGeometry args={[0.05, 0.1, 8]} />
|
|
312
|
+
<meshBasicMaterial color={color} />
|
|
313
|
+
</mesh>
|
|
314
|
+
</group>
|
|
315
|
+
);
|
|
316
|
+
}
|