'use client' import React, { useEffect, useMemo, useRef, useState } from 'react' import { useStore } from 'zustand' import { Badge, Button, Input } from '@djangocfg/ui-core/components' import { cn } from '@djangocfg/ui-core/lib' import { Copy, Trash2 } from 'lucide-react' import { devtoolsStore } from '../store' import type { LogEntry, LogLevel } from '../types' const LEVELS: LogLevel[] = ['debug', 'info', 'success', 'warn', 'error'] const LEVEL_COLORS: Record = { debug: 'text-muted-foreground', info: 'text-blue-500', success: 'text-emerald-500', warn: 'text-amber-500', error: 'text-red-500', } function formatTime(d: Date): string { return d.toTimeString().slice(0, 8) + '.' + String(d.getMilliseconds()).padStart(3, '0') } function Row({ entry }: { entry: LogEntry }) { const [expanded, setExpanded] = useState(false) const hasDetails = Boolean(entry.stack || (entry.data && Object.keys(entry.data).length > 0)) return (
setExpanded((v) => !v) : undefined} >
{formatTime(entry.timestamp)} {entry.level} {entry.source} {entry.message}
{expanded && (
{entry.data && Object.keys(entry.data).length > 0 && (
              {JSON.stringify(entry.data, null, 2)}
            
)} {entry.stack && (
              {entry.stack}
            
)}
)}
) } export function LogsTab({ isActive }: { isActive: boolean }) { const entries = useStore(devtoolsStore, (s) => s.entries) const clearEntries = useStore(devtoolsStore, (s) => s.clearEntries) const [levels, setLevels] = useState>(new Set(LEVELS)) const [search, setSearch] = useState('') const scrollRef = useRef(null) const filtered = useMemo(() => { const q = search.trim().toLowerCase() return entries.filter((e) => { if (!levels.has(e.level)) return false if (!q) return true return ( e.message.toLowerCase().includes(q) || e.source.toLowerCase().includes(q) || (e.data ? JSON.stringify(e.data).toLowerCase().includes(q) : false) ) }) }, [entries, levels, search]) // Follow the tail while the tab is visible useEffect(() => { if (!isActive) return const el = scrollRef.current if (el) el.scrollTop = el.scrollHeight }, [filtered.length, isActive]) const toggleLevel = (level: LogLevel) => setLevels((prev) => { const next = new Set(prev) if (next.has(level)) next.delete(level) else next.add(level) return next }) return (
{/* Toolbar */}
{LEVELS.map((level) => ( ))}
setSearch(e.target.value)} placeholder="Filter…" className="h-6 flex-1 text-xs" /> {filtered.length}
{/* Entries */}
{filtered.length === 0 ? (
No log entries
) : ( filtered.map((entry) => ) )}
) }