'use client'; /** * Main Dashboard Page * Multi-view dashboard for the Claude Cortex memory system */ import { useState, useRef, useEffect } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; import dynamic from 'next/dynamic'; import { useMemoriesWithRealtime, useStats, useAccessMemory, useConsolidate, useProjects, useMemoryLinks, useControlStatus, usePauseMemory, useResumeMemory } from '@/hooks/useMemories'; import { useDashboardStore } from '@/lib/store'; import { useDebouncedValue } from '@/hooks/useDebouncedValue'; import { useSuggestions } from '@/hooks/useSuggestions'; import { MemoryDetail } from '@/components/memory/MemoryDetail'; import { MemoriesView } from '@/components/memories/MemoriesView'; import { NavRail } from '@/components/nav/NavRail'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { InsightsView } from '@/components/insights/InsightsView'; import { Memory } from '@/types/memory'; // Dynamic imports (avoid SSR issues with canvas/WebGL) const KnowledgeGraph = dynamic( () => import('@/components/graph/KnowledgeGraph'), { ssr: false, loading: () => (
Loading Graph...
), } ); const OntologyGraph = dynamic( () => import('@/components/graph/OntologyGraph'), { ssr: false, loading: () => (
Loading Ontology...
), } ); const BrainScene = dynamic( () => import('@/components/brain/BrainScene').then((mod) => mod.BrainScene), { ssr: false, loading: () => (
Loading 3D Brain...
), } ); export default function DashboardPage() { const [searchQuery, setSearchQuery] = useState(''); const [showSuggestions, setShowSuggestions] = useState(false); const [selectedProject, setSelectedProject] = useState(undefined); const [showFilters, setShowFilters] = useState(false); const [typeFilter, setTypeFilter] = useState(undefined); const [categoryFilter, setCategoryFilter] = useState(undefined); const searchInputRef = useRef(null); const suggestionsRef = useRef(null); // Debounce search to avoid API calls on every keystroke const debouncedSearch = useDebouncedValue(searchQuery, 300); // Zustand store const { viewMode, selectedMemory, setSelectedMemory } = useDashboardStore(); // Search suggestions const { data: suggestions = [] } = useSuggestions(searchQuery); // Fetch projects for dropdown const { data: projectsData } = useProjects(); // Close suggestions when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( suggestionsRef.current && !suggestionsRef.current.contains(event.target as Node) && searchInputRef.current && !searchInputRef.current.contains(event.target as Node) ) { setShowSuggestions(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const handleSelectSuggestion = (text: string) => { setSearchQuery(text); setShowSuggestions(false); searchInputRef.current?.focus(); }; // Data fetching with real-time WebSocket updates const { data: memories = [], isLoading: memoriesLoading, isConnected, } = useMemoriesWithRealtime({ limit: 200, query: debouncedSearch || undefined, mode: debouncedSearch ? 'search' : 'recent', project: selectedProject, type: typeFilter, category: categoryFilter, }); const { data: stats, isLoading: _statsLoading } = useStats(selectedProject); const { data: links = [] } = useMemoryLinks(selectedProject); // Mutations const accessMutation = useAccessMemory(); const consolidateMutation = useConsolidate(); // Control status const { data: controlStatus } = useControlStatus(); const _pauseMutation = usePauseMemory(); const _resumeMutation = useResumeMemory(); const isPaused = controlStatus?.paused ?? false; const handleSelectMemory = (memory: Memory | null) => { setSelectedMemory(memory); }; const handleSelectMemoryById = (id: number) => { const memory = memories.find(m => m.id === id); if (memory) { setSelectedMemory(memory); } }; const handleReinforce = (id: number) => { accessMutation.mutate(id); }; const _handleConsolidate = () => { consolidateMutation.mutate(); }; return (
{/* Top Bar */}

🧠 Claude Cortex

{/* Project Selector */} {/* Search Input */}
{ setSearchQuery(e.target.value); setShowSuggestions(true); }} onFocus={() => setShowSuggestions(true)} onKeyDown={(e) => { if (e.key === 'Escape') { setShowSuggestions(false); } }} className="w-64 bg-slate-800 border-slate-700 text-white placeholder:text-slate-400 focus:ring-blue-500" /> {/* Suggestions dropdown */} {showSuggestions && suggestions.length > 0 && (
{suggestions.map((suggestion, index) => ( ))}
)}
{/* Filter Toggle */}
{memories.length} memories
{/* Filter Bar (collapsible) */} {showFilters && (
{/* Type filters */}
Type: {['short_term', 'long_term', 'episodic'].map((type) => ( ))}
{/* Category filters */}
Category: {['architecture', 'pattern', 'error', 'learning', 'preference', 'context'].map((cat) => ( ))}
{/* Clear filters */} {(typeFilter || categoryFilter) && ( <>
)}
)} {/* Main Content */}
{/* Active View */} {viewMode === 'brain' && ( memoriesLoading ? (
Loading memories...
) : ( ) )} {viewMode === 'graph' && ( )} {viewMode === 'memories' && ( )} {viewMode === 'insights' && ( )} {viewMode === 'ontology' && ( )}
{/* Right Detail Panel */} {selectedMemory && (
setSelectedMemory(null)} onReinforce={handleReinforce} onSelectMemory={handleSelectMemoryById} />
)}
); }