import { useState, useEffect, useCallback } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import {
FileText, Shield, Copy, Check, ChevronLeft, Layers, Search,
Database, Hash, Activity, Code,
} from 'lucide-react'
const API_BASE = ''
export default function FileDetail({ fileId, onClear }) {
const [files, setFiles] = useState([])
const [selectedId, setSelectedId] = useState(fileId)
const [meta, setMeta] = useState(null)
const [summary, setSummary] = useState(null)
const [capabilities, setCapabilities] = useState(null)
const [state, setState] = useState(null)
const [loading, setLoading] = useState(false)
const [copied, setCopied] = useState(false)
const [activeTab, setActiveTab] = useState('meta')
// Fetch file list
useEffect(() => {
const fetchFiles = async () => {
try {
const res = await fetch(`${API_BASE}/files`)
const data = await res.json()
setFiles(data.files || [])
if (!selectedId && data.files?.length > 0) {
setSelectedId(data.files[0].file_id)
}
} catch {
setFiles([])
}
}
fetchFiles()
}, [])
// Update selectedId when fileId prop changes
useEffect(() => {
if (fileId) setSelectedId(fileId)
}, [fileId])
// Fetch detail data for selected file
const fetchDetail = useCallback(async (id) => {
if (!id) return
setLoading(true)
setMeta(null)
setSummary(null)
setCapabilities(null)
setState(null)
try {
const [mRes, sRes, cRes, stRes] = await Promise.all([
fetch(`${API_BASE}/meta/${id}`).then(r => r.json()).catch(() => null),
fetch(`${API_BASE}/summary/${id}`).then(r => r.json()).catch(() => null),
fetch(`${API_BASE}/capabilities/${id}`).then(r => r.json()).catch(() => null),
fetch(`${API_BASE}/superpose/state/${id}`).then(r => r.json()).catch(() => null),
])
setMeta(mRes)
setSummary(sRes)
setCapabilities(cRes)
setState(stRes)
} finally {
setLoading(false)
}
}, [])
useEffect(() => {
fetchDetail(selectedId)
}, [selectedId, fetchDetail])
const copyUrl = () => {
navigator.clipboard?.writeText(`${API_BASE}/meta/${selectedId}`)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
const selectedFile = files.find(f => f.file_id === selectedId)
const isLive = state?.session_status === 'live'
return (
{/* File selector */}
{onClear && (
)}
{files.map(f => (
))}
{loading && (
Fetching file data...
)}
{!loading && meta && (
<>
{/* File header */}
{meta.meta?.filename || selectedFile?.filename}
{selectedId}
{isLive ? '● LIVE' : '● OFFLINE'}
{/* Real measured metadata grid */}
{[
['Size', meta.meta?.size_human || '—'],
['Lines', meta.meta?.total_lines || '—'],
['Format', meta.meta?.format || '—'],
['Chunks', meta.meta?.total_chunks || '—'],
['Words', meta.meta?.total_words || '—'],
['Chars', meta.meta?.total_chars || '—'],
['Entropy', meta.meta?.entropy || '—'],
['Merkle Root', meta.meta?.merkle_root?.slice(0, 12) + '...' || '—'],
].map(([label, value]) => (
))}
{state && (
Index Size
{state.compression_ratio || '—'}
Queries
{state.total_queries || 0}
Last Access
{state.last_access ? new Date(state.last_access * 1000).toLocaleTimeString() : 'Never'}
)}
{/* Tabs */}
{[
{ id: 'meta', label: 'Meta', icon: FileText },
{ id: 'summary', label: 'Summary', icon: Activity },
{ id: 'capabilities', label: 'Capabilities', icon: Shield },
{ id: 'chunks', label: 'Chunks', icon: Layers },
].map(tab => (
))}
{/* Tab content */}
{/* Meta tab */}
{activeTab === 'meta' && meta && (
Metadata
{JSON.stringify(meta, null, 2)}
)}
{/* Summary tab */}
{activeTab === 'summary' && summary && (
{/* Semantic chunks */}
{summary.semantic_chunks && (
Semantic Chunks ({summary.semantic_chunks.length})
{summary.semantic_chunks.map(chunk => (
#{chunk.idx}
{chunk.type}
L{chunk.lines}
{chunk.size}B
))}
)}
{/* Functions */}
{summary.functions && summary.functions.length > 0 && (
Functions ({summary.functions.length})
{summary.functions.map((fn, i) => (
L{fn.line}
{fn.symbol}
))}
)}
{/* Top words */}
{summary.top_words && (
Top Words
{summary.top_words.map((w, i) => (
{w.word} ×{w.count}
))}
)}
{/* Section headers */}
{summary.section_headers && summary.section_headers.length > 0 && (
Sections ({summary.section_headers.length})
{summary.section_headers.map((s, i) => (
{s.header} ×{s.count}
))}
)}
)}
{/* Capabilities tab */}
{activeTab === 'capabilities' && capabilities && (
{capabilities.total} Capabilities
{capabilities.capabilities?.map(cap => (
{cap.name}
))}
)}
{/* Chunks tab */}
{activeTab === 'chunks' && summary?.semantic_chunks && (
)}
>
)}
{!loading && !meta && (
{files.length === 0 ? 'No files indexed.' : 'Select a file to inspect.'}
)}
)
}
function ChunkViewer({ fileId, chunks }) {
const [selectedChunk, setSelectedChunk] = useState(null)
const [chunkData, setChunkData] = useState(null)
const [loadingChunk, setLoadingChunk] = useState(false)
const fetchChunk = async (idx) => {
setSelectedChunk(idx)
setLoadingChunk(true)
setChunkData(null)
try {
const res = await fetch(`${API_BASE}/chunk/${fileId}/${idx}`)
const data = await res.json()
setChunkData(data)
} catch {
setChunkData({ error: 'Failed to fetch chunk' })
} finally {
setLoadingChunk(false)
}
}
return (
{/* Chunk list */}
Chunks ({chunks.length})
{chunks.map(chunk => (
))}
{/* Chunk content */}
{loadingChunk && (
Fetching chunk...
)}
{!loadingChunk && !selectedChunk && (
Select a chunk to view its content.
)}
{!loadingChunk && chunkData && (
Chunk #{selectedChunk}
{chunkData.error && {chunkData.error}}
{chunkData.content || chunkData.chunk_content || chunkData.text || JSON.stringify(chunkData, null, 2)}
)}
)
}