import React, { useState } from 'react' import { ChevronRight, ChevronDown, FileText, Clock, Users, Tag, Brain } from 'lucide-react' import { Badge } from '@/components/ui/badge' import { Memory, MemoryCategory } from '@/types' interface MemoryTreeViewProps { memories: Memory[] onMemoryClick?: (memory: Memory) => void extractTitle?: (content: string, memory?: Memory) => string extractTags?: (memory: Memory) => string[] getTagColor?: (tag: string) => { bg: string; text: string; border: string } } const categoryColors: Record = { personal: "text-blue-400 bg-blue-500/20 border-blue-500/30", work: "text-green-400 bg-green-500/20 border-green-500/30", code: "text-purple-400 bg-purple-500/20 border-purple-500/30", research: "text-orange-400 bg-orange-500/20 border-orange-500/30", conversations: "text-pink-400 bg-pink-500/20 border-pink-500/30", preferences: "text-gray-400 bg-gray-500/20 border-gray-500/30" } export function MemoryTreeView({ memories, onMemoryClick, extractTitle = (content: string) => content.substring(0, 50) + (content.length > 50 ? '...' : ''), extractTags = (memory: Memory) => memory.tags || [], getTagColor = (tag: string) => ({ bg: '#374151', text: '#9CA3AF', border: '#4B5563' }) }: MemoryTreeViewProps) { const [expandedMemories, setExpandedMemories] = useState>(new Set()) const [expandedProjects, setExpandedProjects] = useState>(new Set()) // Group memories by project const memoriesByProject = memories.reduce((acc, memory) => { const project = memory.project || 'default' if (!acc[project]) acc[project] = [] acc[project].push(memory) return acc }, {} as Record) // Build parent-child relationships using related_memories field const memoryMap = new Map(memories.map(m => [m.id, m])) // Helper function to get related memories const getRelatedMemories = (memoryId: string): Memory[] => { const memory = memoryMap.get(memoryId) if (!memory || !memory.metadata) return [] // Check for related_memories in metadata or parse from tags const relatedIds = Array.isArray(memory.metadata.related_memories) ? memory.metadata.related_memories : [] return relatedIds.map(id => memoryMap.get(id)).filter(Boolean) as Memory[] } // Helper function to get memories that reference this memory const getChildMemories = (memoryId: string): Memory[] => { return memories.filter(memory => { if (!memory.metadata) return false const relatedIds = Array.isArray(memory.metadata.related_memories) ? memory.metadata.related_memories : [] return relatedIds.includes(memoryId) }) } // Get root memories (those not referenced by others as related) const getRootMemories = (projectMemories: Memory[]): Memory[] => { const childIds = new Set() projectMemories.forEach(memory => { if (memory.metadata?.related_memories && Array.isArray(memory.metadata.related_memories)) { memory.metadata.related_memories.forEach(id => childIds.add(id)) } }) return projectMemories.filter(memory => !childIds.has(memory.id)) } // Find memories with shared tags as secondary connections const getTagConnections = (memory: Memory): Memory[] => { const memoryTags = extractTags(memory) if (memoryTags.length === 0) return [] return memories.filter(other => { if (other.id === memory.id) return false const otherTags = extractTags(other) return memoryTags.some(tag => otherTags.includes(tag)) }).slice(0, 3) // Limit to 3 connections to avoid clutter } const toggleMemory = (memoryId: string) => { const newExpanded = new Set(expandedMemories) if (newExpanded.has(memoryId)) { newExpanded.delete(memoryId) } else { newExpanded.add(memoryId) } setExpandedMemories(newExpanded) } const toggleProject = (project: string) => { const newExpanded = new Set(expandedProjects) if (newExpanded.has(project)) { newExpanded.delete(project) } else { newExpanded.add(project) } setExpandedProjects(newExpanded) } const getCategoryIcon = (category: MemoryCategory | undefined) => { switch (category) { case 'personal': return '👤' case 'work': return '💼' case 'code': return '💻' case 'research': return '🔬' case 'conversations': return '💬' case 'preferences': return '⚙️' default: return '📄' } } const formatRelativeTime = (timestamp: string) => { const date = new Date(timestamp) const now = new Date() const diffMs = now.getTime() - date.getTime() const diffMins = Math.floor(diffMs / 60000) const diffHours = Math.floor(diffMins / 60) const diffDays = Math.floor(diffHours / 24) if (diffMins < 60) return `${diffMins}m ago` if (diffHours < 24) return `${diffHours}h ago` if (diffDays < 7) return `${diffDays}d ago` return date.toLocaleDateString() } const renderMemory = (memory: Memory, level: number = 0) => { const relatedMemories = getRelatedMemories(memory.id) const childMemories = getChildMemories(memory.id) const tagConnections = getTagConnections(memory) const allConnections = [...relatedMemories, ...childMemories] const hasConnections = allConnections.length > 0 || tagConnections.length > 0 const isExpanded = expandedMemories.has(memory.id) const memoryTags = extractTags(memory) const title = extractTitle(memory.content, memory) return (
0 ? 'ml-' + (level * 6) : ''} `} onClick={() => onMemoryClick?.(memory)} > {hasConnections && ( )} {!hasConnections &&
} {title} {/* Category badge */} {memory.category && ( {getCategoryIcon(memory.category)} )} {/* Tag count */} {memoryTags.length > 0 && (
{memoryTags.length}
)} {/* Connections count */} {allConnections.length > 0 && (
{allConnections.length}
)} {/* Time */}
{formatRelativeTime(memory.timestamp)}
{isExpanded && (
{/* Direct related memories */} {relatedMemories.length > 0 && (
🔗 Related
{relatedMemories.map(related => ( {renderMemory(related, level + 1)} ))}
)} {/* Child memories (those that reference this memory) */} {childMemories.length > 0 && (
📎 References
{childMemories.map(child => ( {renderMemory(child, level + 1)} ))}
)} {/* Tag-based connections */} {tagConnections.length > 0 && (
🏷️ Similar Tags
{tagConnections.map(connection => (
{ e.stopPropagation() onMemoryClick?.(connection) }} > {extractTitle(connection.content, connection)}
{extractTags(connection).slice(0, 2).map(tag => ( {tag} ))}
))}
)}
)}
) } return (
{Object.entries(memoriesByProject).map(([project, projectMemories]) => { const isProjectExpanded = expandedProjects.has(project) ?? true const rootMemories = getRootMemories(projectMemories) return (
{isProjectExpanded && (
{rootMemories.length > 0 ? ( rootMemories.map(memory => ( {renderMemory(memory)} )) ) : (

No memories in this project

)}
)}
) })}
) }