80 lines
3.4 KiB
TypeScript
80 lines
3.4 KiB
TypeScript
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
import { useLogger, LogMessage, LogType } from '../contexts/LoggingContext';
|
|
|
|
const LOG_COLORS: Record<LogType, string> = {
|
|
info: 'text-gray-300',
|
|
success: 'text-green-400',
|
|
error: 'text-red-400',
|
|
warn: 'text-yellow-400',
|
|
};
|
|
|
|
const DebugConsole: React.FC = () => {
|
|
const { logs, clearLogs } = useLogger();
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [copyStatus, setCopyStatus] = useState('Copy');
|
|
const logsEndRef = useRef<HTMLDivElement>(null);
|
|
|
|
const scrollToBottom = () => {
|
|
logsEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
};
|
|
|
|
useEffect(scrollToBottom, [logs]);
|
|
|
|
const handleCopy = () => {
|
|
const logText = logs.map(log => `[${log.timestamp}] [${log.type.toUpperCase()}] ${log.message}`).join('\n');
|
|
navigator.clipboard.writeText(logText).then(() => {
|
|
setCopyStatus('Copied!');
|
|
setTimeout(() => setCopyStatus('Copy'), 2000);
|
|
}, () => {
|
|
setCopyStatus('Failed!');
|
|
setTimeout(() => setCopyStatus('Copy'), 2000);
|
|
});
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
className="fixed bottom-4 right-4 bg-purple-700 hover:bg-purple-800 text-white rounded-full p-3 shadow-lg z-50 transition-transform hover:scale-110"
|
|
aria-label="Toggle Debug Console"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
</svg>
|
|
</button>
|
|
|
|
{isOpen && (
|
|
<div className="fixed bottom-0 left-0 right-0 h-1/3 bg-gray-900/95 backdrop-blur-sm border-t-2 border-purple-800 z-40 flex flex-col p-2 shadow-2xl">
|
|
<div className="flex items-center justify-between mb-2 px-2 flex-shrink-0">
|
|
<h3 className="font-teko text-2xl text-purple-300 tracking-wide">DEBUG CONSOLE</h3>
|
|
<div className="flex items-center gap-2">
|
|
<button onClick={handleCopy} className="text-xs bg-gray-700 hover:bg-gray-600 text-gray-300 px-3 py-1 rounded-md transition-colors">{copyStatus}</button>
|
|
<button onClick={clearLogs} className="text-xs bg-gray-700 hover:bg-gray-600 text-gray-300 px-3 py-1 rounded-md transition-colors">Clear</button>
|
|
<button onClick={() => setIsOpen(false)} className="text-gray-400 hover:text-white">×</button>
|
|
</div>
|
|
</div>
|
|
<div className="overflow-y-auto flex-grow bg-black/50 p-2 rounded-md font-mono text-sm">
|
|
{logs.length === 0 ? (
|
|
<p className="text-gray-500">No logs yet. Start using the app to see messages here.</p>
|
|
) : (
|
|
logs.map((log, index) => (
|
|
<div key={index} className="flex">
|
|
<span className="text-gray-500 mr-2 flex-shrink-0">{log.timestamp}</span>
|
|
<span className={`${LOG_COLORS[log.type]} whitespace-pre-wrap break-all`}>
|
|
<span className='font-bold mr-2'>[{log.type.toUpperCase()}]</span>
|
|
{log.message}
|
|
</span>
|
|
</div>
|
|
))
|
|
)}
|
|
<div ref={logsEndRef} />
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default DebugConsole;
|