'use client' import { useEffect, useState } from 'react' import { Brain, Search, ChevronLeft, ChevronRight, Hash, Clock, Tag, FileText } from 'lucide-react' interface MemEntry { hash?: string; parent?: string; action?: string; entity?: string details?: string; timestamp?: string; lineNumber: number; parse_error?: boolean; raw?: string } const ACTION_COLORS: Record = { 'code-change': '#2979FF', 'session-start': '#22c55e', 'session-end': '#888', 'file-create': '#F5A623', 'commit': '#9C27B0', 'deploy': '#FF1D6C', 'memory-sync': '#06b6d4', 'agent-dm': '#2979FF', 'broadcast': '#FF1D6C', } function timeAgo(iso?: string) { if (!iso) return '—' try { const diff = Date.now() - new Date(iso).getTime() const m = Math.floor(diff / 60000), h = Math.floor(m / 60), d = Math.floor(h / 24) if (d > 0) return `${d}d ago`; if (h > 0) return `${h}h ago`; if (m > 0) return `${m}m ago`; return 'just now' } catch { return iso } } export default function MemoryPage() { const [data, setData] = useState<{ entries: MemEntry[]; total: number; page: number; pages: number; ledgerPath: string } | null>(null) const [search, setSearch] = useState('') const [page, setPage] = useState(1) const [loading, setLoading] = useState(true) const [view, setView] = useState<'ledger' | 'files'>('ledger') const [files, setFiles] = useState([]) const load = async () => { setLoading(true) try { const r = await fetch(`/api/memory?view=${view}&search=${encodeURIComponent(search)}&page=${page}`) const d = await r.json() if (view === 'files') setFiles(d.files || []) else setData(d) } finally { setLoading(false) } } useEffect(() => { load() }, [view, page]) const handleSearch = (e: React.FormEvent) => { e.preventDefault(); setPage(1); load() } return (

Memory Ledger

PS-SHA∞ hash-chain · {data?.total ?? 0} entries · {data?.ledgerPath?.replace('/Users/alexa', '~')}

{/* View tabs */}
{[{ key: 'ledger', label: 'Journal Entries', icon: Hash }, { key: 'files', label: 'Context Files', icon: FileText }].map(({ key, label, icon: Icon }) => ( ))}
{view === 'ledger' && ( <>
setSearch(e.target.value)} placeholder="Search memory entries…" style={{ width: '100%', background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 8, padding: '9px 12px 9px 34px', color: '#fff', fontSize: 13, outline: 'none', boxSizing: 'border-box' }} />
{loading ? (
Loading…
) : ( <>
{(data?.entries || []).map((entry, i) => { if (entry.parse_error) return (
Parse error: {entry.raw}
) const color = ACTION_COLORS[entry.action || ''] || '#888' return (
{entry.action || 'entry'}
{entry.entity &&
{entry.entity}
} {entry.details &&
{entry.details}
}
{entry.hash && ( #{entry.hash.slice(0, 8)} )} {entry.timestamp && ( {timeAgo(entry.timestamp)} )} #{entry.lineNumber}
) })}
{/* Pagination */} {(data?.pages || 1) > 1 && (
Page {data?.page} of {data?.pages}
)} )} )} {view === 'files' && (
{loading ?
Loading…
: files.map((f, i) => (
~/.blackroad/memory/{f.path} {(f.size / 1024).toFixed(1)}KB · {timeAgo(f.modifiedAt)}
                  {f.preview}
                
))}
)}
) }