'use client';
/**
* Memory Grid
* Organizes memories in a grid layout within each memory bank section
* Automatically switches between MemoryCell and QuantumCell based on salience
*/
import { useMemo } from 'react';
import { Memory, MemoryType } from '@/types/memory';
import { MemoryCell } from './MemoryCell';
import { QuantumCell } from './QuantumCell';
// Salience threshold for quantum cells
const QUANTUM_THRESHOLD = 0.7;
interface GridConfig {
columns: number;
cellWidth: number;
cellHeight: number;
horizontalGap: number;
verticalGap: number;
}
interface MemoryGridProps {
memories: Memory[];
memoryType: MemoryType;
chipWidth: number;
chipHeight: number;
selectedMemory: Memory | null;
onSelectMemory: (memory: Memory | null) => void;
}
// Calculate grid position for a memory at given index
function calculateGridPosition(
index: number,
config: GridConfig,
sectionOffset: { x: number; y: number; z: number },
sectionWidth: number
): [number, number, number] {
const { columns, cellWidth, horizontalGap, verticalGap } = config;
const col = index % columns;
const row = Math.floor(index / columns);
// Calculate total grid width
const gridWidth = columns * cellWidth + (columns - 1) * horizontalGap;
const startX = -gridWidth / 2 + cellWidth / 2;
const x = sectionOffset.x + startX + col * (cellWidth + horizontalGap);
const y = sectionOffset.y - row * (cellWidth * 0.6 + verticalGap);
const z = sectionOffset.z;
return [x, y, z];
}
export function MemoryGrid({
memories,
memoryType,
chipWidth,
chipHeight,
selectedMemory,
onSelectMemory,
}: MemoryGridProps) {
const sectionHeight = chipHeight / 3;
const halfChipHeight = chipHeight / 2;
// Grid configuration
const gridConfig: GridConfig = useMemo(
() => ({
columns: 8,
cellWidth: 1.0,
cellHeight: 0.6,
horizontalGap: 0.4,
verticalGap: 0.3,
}),
[]
);
// Section offset based on memory type
const sectionOffset = useMemo(() => {
switch (memoryType) {
case 'short_term':
return {
x: 0,
y: halfChipHeight - sectionHeight * 0.35,
z: 0.2,
};
case 'long_term':
return {
x: 0,
y: -halfChipHeight + sectionHeight * 0.65,
z: 0.2,
};
case 'episodic':
default:
return {
x: 0,
y: 0,
z: 0.2,
};
}
}, [memoryType, halfChipHeight, sectionHeight]);
// Filter memories by type
const filteredMemories = useMemo(() => {
return memories.filter((m) => m.type === memoryType);
}, [memories, memoryType]);
// Sort memories by salience (highest first) for better visual hierarchy
const sortedMemories = useMemo(() => {
return [...filteredMemories].sort((a, b) => b.salience - a.salience);
}, [filteredMemories]);
return (
{sortedMemories.map((memory, index) => {
const position = calculateGridPosition(
index,
gridConfig,
sectionOffset,
chipWidth
);
const isQuantum = memory.salience >= QUANTUM_THRESHOLD;
if (isQuantum) {
return (
);
}
return (
);
})}
);
}
// Get grid position for a specific memory (used by pulse animations)
export function getMemoryGridPosition(
memory: Memory,
memories: Memory[],
chipWidth: number,
chipHeight: number
): [number, number, number] | null {
const sectionHeight = chipHeight / 3;
const halfChipHeight = chipHeight / 2;
const gridConfig: GridConfig = {
columns: 8,
cellWidth: 1.0,
cellHeight: 0.6,
horizontalGap: 0.4,
verticalGap: 0.3,
};
// Get section offset
let sectionOffset: { x: number; y: number; z: number };
switch (memory.type) {
case 'short_term':
sectionOffset = {
x: 0,
y: halfChipHeight - sectionHeight * 0.35,
z: 0.2,
};
break;
case 'long_term':
sectionOffset = {
x: 0,
y: -halfChipHeight + sectionHeight * 0.65,
z: 0.2,
};
break;
case 'episodic':
default:
sectionOffset = {
x: 0,
y: 0,
z: 0.2,
};
}
// Find index of this memory in its section (sorted by salience)
const sectionMemories = memories
.filter((m) => m.type === memory.type)
.sort((a, b) => b.salience - a.salience);
const index = sectionMemories.findIndex((m) => m.id === memory.id);
if (index === -1) return null;
return calculateGridPosition(index, gridConfig, sectionOffset, chipWidth);
}
// Episodic memories need special handling since they're split left/right of core
export function EpisodicMemoryGrid({
memories,
chipWidth,
chipHeight,
coreWidth,
selectedMemory,
onSelectMemory,
}: {
memories: Memory[];
chipWidth: number;
chipHeight: number;
coreWidth: number;
selectedMemory: Memory | null;
onSelectMemory: (memory: Memory | null) => void;
}) {
const episodicMemories = useMemo(() => {
return memories
.filter((m) => m.type === 'episodic')
.sort((a, b) => b.salience - a.salience);
}, [memories]);
// Split memories between left and right of core
const leftMemories = episodicMemories.filter((_, i) => i % 2 === 0);
const rightMemories = episodicMemories.filter((_, i) => i % 2 === 1);
const gridConfig: GridConfig = {
columns: 3,
cellWidth: 1.0,
cellHeight: 0.6,
horizontalGap: 0.4,
verticalGap: 0.3,
};
const leftOffset = {
x: -coreWidth - 2,
y: 0.5,
z: 0.2,
};
const rightOffset = {
x: coreWidth + 2,
y: 0.5,
z: 0.2,
};
return (
{/* Left side */}
{leftMemories.map((memory, index) => {
const position = calculateGridPosition(
index,
gridConfig,
leftOffset,
chipWidth / 2 - coreWidth
);
const isQuantum = memory.salience >= QUANTUM_THRESHOLD;
if (isQuantum) {
return (
);
}
return (
);
})}
{/* Right side */}
{rightMemories.map((memory, index) => {
const position = calculateGridPosition(
index,
gridConfig,
rightOffset,
chipWidth / 2 - coreWidth
);
const isQuantum = memory.salience >= QUANTUM_THRESHOLD;
if (isQuantum) {
return (
);
}
return (
);
})}
);
}