Spaces:
Build error
Build error
| import { useState, useEffect, useRef } from 'react' | |
| import Editor from '@monaco-editor/react' | |
| import { motion } from 'framer-motion' | |
| import { | |
| FaPlay, FaSave, FaDownload, FaUpload, FaCopy, FaPaste, | |
| FaUndo, FaRedo, FaSearch, FaReplace, FaCog, FaExpand, | |
| FaCompress, FaTerminal, FaEye, FaCode, FaFile, FaFolder, | |
| FaPlus, FaMinus, FaTimes, FaCheck, FaSync, FaBug, | |
| FaRocket, FaShieldAlt, FaChartLine, FaDatabase, FaCloud, | |
| FaServer, FaGitAlt, FaLink, FaLock, FaUnlock, FaShare, | |
| FaStar, FaRegStar, FaBookmark, FaFlag, FaTag, FaTags, | |
| FaCalendar, FaClock, FaUser, FaUsers, FaComment, FaComments, | |
| FaHeart, FaRegHeart, FaThumbsUp, FaThumbsDown, FaEyeSlash, | |
| FaEdit, FaTrash, FaFileCode, FaFileAlt, FaFileArchive, | |
| FaFileImage, FaFileVideo, FaFileAudio, FaFilePdf, FaFileExcel, | |
| FaFileWord, FaFilePowerpoint, FaFileCsv, FaFileZip, FaFileMedical | |
| } from 'react-icons/fa' | |
| import toast from 'react-hot-toast' | |
| export default function CodeEditor({ language, theme }) { | |
| const [code, setCode] = useState(`// Welcome to AI Dev Studio | |
| function helloWorld() { | |
| console.log("Hello, World!"); | |
| return "Welcome to the future of coding!"; | |
| } | |
| helloWorld();`) | |
| const [currentLanguage, setCurrentLanguage] = useState('javascript') | |
| const [theme, setTheme] = useState('vs-dark') | |
| const [isFullscreen, setIsFullscreen] = useState(false) | |
| const [showTerminal, setShowTerminal] = useState(false) | |
| const [terminalOutput, setTerminalOutput] = useState([]) | |
| const [fontSize, setFontSize] = useState(14) | |
| const [wordWrap, setWordWrap] = useState(true) | |
| const [minimap, setMinimap] = useState(true) | |
| const [lineNumbers, setLineNumbers] = useState(true) | |
| const editorRef = useRef(null) | |
| const languages = [ | |
| { value: 'javascript', label: 'JavaScript' }, | |
| { value: 'typescript', label: 'TypeScript' }, | |
| { value: 'python', label: 'Python' }, | |
| { value: 'java', label: 'Java' }, | |
| { value: 'cpp', label: 'C++' }, | |
| { value: 'csharp', label: 'C#' }, | |
| { value: 'php', label: 'PHP' }, | |
| { value: 'ruby', label: 'Ruby' }, | |
| { value: 'go', label: 'Go' }, | |
| { value: 'rust', label: 'Rust' }, | |
| { value: 'sql', label: 'SQL' }, | |
| { value: 'html', label: 'HTML' }, | |
| { value: 'css', label: 'CSS' }, | |
| { value: 'json', label: 'JSON' }, | |
| { value: 'xml', label: 'XML' }, | |
| { value: 'yaml', label: 'YAML' }, | |
| { value: 'markdown', label: 'Markdown' }, | |
| { value: 'dockerfile', label: 'Dockerfile' }, | |
| { value: 'shell', label: 'Shell' }, | |
| { value: 'plaintext', label: 'Plain Text' } | |
| ] | |
| const handleRunCode = () => { | |
| const output = `> Running ${currentLanguage} code...\n> Code executed successfully!\n> Output: Hello, World!` | |
| setTerminalOutput(prev => [...prev, { type: 'success', message: output, timestamp: new Date() }]) | |
| setShowTerminal(true) | |
| toast.success('Code executed successfully!') | |
| } | |
| const handleSaveCode = () => { | |
| toast.success('Code saved successfully!') | |
| } | |
| const handleDownloadCode = () => { | |
| const blob = new Blob([code], { type: 'text/plain' }) | |
| const url = URL.createObjectURL(blob) | |
| const a = document.createElement('a') | |
| a.href = url | |
| a.download = `code.${getFileExtension()}` | |
| a.click() | |
| toast.success('Code downloaded!') | |
| } | |
| const getFileExtension = () => { | |
| const extensions = { | |
| javascript: 'js', | |
| typescript: 'ts', | |
| python: 'py', | |
| java: 'java', | |
| cpp: 'cpp', | |
| csharp: 'cs', | |
| php: 'php', | |
| ruby: 'rb', | |
| go: 'go', | |
| rust: 'rs', | |
| sql: 'sql', | |
| html: 'html', | |
| css: 'css', | |
| json: 'json', | |
| xml: 'xml', | |
| yaml: 'yaml', | |
| markdown: 'md', | |
| dockerfile: 'dockerfile', | |
| shell: 'sh', | |
| plaintext: 'txt' | |
| } | |
| return extensions[currentLanguage] || 'txt' | |
| } | |
| const formatCode = () => { | |
| if (editorRef.current) { | |
| editorRef.current.getAction('editor.action.formatDocument').run() | |
| toast.success('Code formatted!') | |
| } | |
| } | |
| const toggleFullscreen = () => { | |
| setIsFullscreen(!isFullscreen) | |
| } | |
| const clearTerminal = () => { | |
| setTerminalOutput([]) | |
| toast.success('Terminal cleared!') | |
| } | |
| return ( | |
| <div className={`flex flex-col h-full ${isFullscreen ? 'fixed inset-0 z-50 bg-gray-900' : ''}`}> | |
| {/* Editor Header */} | |
| <div className="flex items-center justify-between p-3 bg-gray-800 border-b border-gray-700"> | |
| <div className="flex items-center space-x-reverse space-x-3"> | |
| <FaCode className="w-5 h-5 text-primary-400" /> | |
| <select | |
| value={currentLanguage} | |
| onChange={(e) => setCurrentLanguage(e.target.value)} | |
| className="px-3 py-1 bg-gray-700 text-white rounded-lg text-sm focus:outline-none" | |
| > | |
| {languages.map(lang => ( | |
| <option key={lang.value} value={lang.value}>{lang.label}</option> | |
| ))} | |
| </select> | |
| <span className="text-xs text-gray-400"> | |
| {code.split('\n').length} lines • {code.length} characters | |
| </span> | |
| </div> | |
| <div className="flex items-center space-x-reverse space-x-2"> | |
| <button | |
| onClick={formatCode} | |
| className="p-2 bg-gray-700 text-white rounded-lg hover:bg-gray-600 transition-all" | |
| title="Format Code" | |
| > | |
| <FaSync className="w-4 h-4" /> | |
| </button> | |
| <button | |
| onClick={handleSaveCode} | |
| className="p-2 bg-gray-700 text-white rounded-lg hover:bg-gray-600 transition-all" | |
| title="Save" | |
| > | |
| <FaSave className="w-4 h-4" /> | |
| </button> | |
| <button | |
| onClick={handleDownloadCode} | |
| className="p-2 bg-gray-700 text-white rounded-lg hover:bg-gray-600 transition-all" | |
| title="Download" | |
| > | |
| <FaDownload className="w-4 h-4" /> | |
| </button> | |
| <button | |
| onClick={handleRunCode} | |
| className="p-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-all" | |
| title="Run Code" | |
| > | |
| <FaPlay className="w-4 h-4" /> | |
| </button> | |
| <button | |
| onClick={() => setShowTerminal(!showTerminal)} | |
| className={`p-2 rounded-lg transition-all ${ | |
| showTerminal ? 'bg-primary-600 text-white' : 'bg-gray-700 text-white hover:bg-gray-600' | |
| }`} | |
| title="Toggle Terminal" | |
| > | |
| <FaTerminal className="w-4 h-4" /> | |
| </button> | |
| <button | |
| onClick={toggleFullscreen} | |
| className="p-2 bg-gray-700 text-white rounded-lg hover:bg-gray-600 transition-all" | |
| title="Toggle Fullscreen" | |
| > | |
| {isFullscreen ? <FaCompress className="w-4 h-4" /> : <FaExpand className="w-4 h-4" />} | |
| </button> | |
| </div> | |
| </div> | |
| {/* Editor Settings Bar */} | |
| <div className="flex items-center justify-between px-3 py-2 bg-gray-750 border-b border-gray-700"> | |
| <div className="flex items-center space-x-reverse space-x-3"> | |
| <label className="flex items-center space-x-reverse space-x-2 text-xs text-gray-400"> | |
| <span>Font Size:</span> | |
| <input | |
| type="number" | |
| value={fontSize} | |
| onChange={(e) => setFontSize(parseInt(e.target.value))} | |
| min="10" | |
| max="24" | |
| className="w-12 px-1 py-0.5 bg-gray-700 text-white rounded" | |
| /> | |
| </label> | |
| <label className="flex items-center space-x-reverse space-x-2 text-xs text-gray-400"> | |
| <input | |
| type="checkbox" | |
| checked={wordWrap} | |
| onChange={(e) => setWordWrap(e.target.checked)} | |
| className="rounded" | |
| /> | |
| <span>Word Wrap</span> | |
| </label> | |
| <label className="flex items-center space-x-reverse space-x-2 text-xs text-gray-400"> | |
| <input | |
| type="checkbox" | |
| checked={minimap} | |
| onChange={(e) => setMinimap(e.target.checked)} | |
| className="rounded" | |
| /> | |
| <span>Minimap</span> | |
| </label> | |
| <label className="flex items-center space-x-reverse space-x-2 text-xs text-gray-400"> | |
| <input | |
| type="checkbox" | |
| checked={lineNumbers} | |
| onChange={(e) => setLineNumbers(e.target.checked)} | |
| className="rounded" | |
| /> | |
| <span>Line Numbers</span> | |
| </label> | |
| </div> | |
| </div> | |
| {/* Code Editor */} | |
| <div className="flex-1 overflow-hidden"> | |
| <Editor | |
| height="100%" | |
| language={currentLanguage} | |
| value={code} | |
| onChange={(value) => setCode(value || '')} | |
| theme={theme} | |
| onMount={(editor) => { | |
| editorRef.current = editor | |
| editor.updateOptions({ | |
| fontSize, | |
| wordWrap: wordWrap ? 'on' : 'off', | |
| minimap: { enabled: minimap }, | |
| lineNumbers: lineNumbers ? 'on' : 'off', | |
| automaticLayout: true, | |
| scrollBeyondLastLine: false, | |
| renderWhitespace: 'selection', | |
| bracketPairColorization: { enabled: true }, | |
| guides: { | |
| bracketPairs: true, | |
| indentation: true | |
| } | |
| }) | |
| options={{ | |
| selectOnLineNumbers: true, | |
| roundedSelection: false, | |
| readOnly: false, | |
| cursorStyle: 'line', | |
| automaticLayout: true, | |
| fontFamily: 'JetBrains Mono, Fira Code, Consolas, monospace', | |
| fontLigatures: true | |
| /> | |
| </div> | |
| {/* Terminal */} | |
| <AnimatePresence> | |
| {showTerminal && ( | |
| <motion.div | |
| initial={{ height: 0, opacity: 0 }} | |
| animate={{ height: 200, opacity: 1 }} | |
| exit={{ height: 0, opacity: 0 }} | |
| className="bg-gray-900 border-t border-gray-700" | |
| > | |
| <div className="flex items-center justify-between p-2 bg-gray-800"> | |
| <div className="flex items-center space-x-reverse space-x-2"> | |
| <FaTerminal className="w-4 h-4 text-primary-400" /> | |
| <span className="text-sm text-gray-300">Terminal</span> | |
| </div> | |
| <button | |
| onClick={clearTerminal} | |
| className="p-1 bg-gray-700 text-white rounded hover:bg-gray-600 transition-all" | |
| > | |
| <FaTrash className="w-3 h-3" /> | |
| </button> | |
| </div> | |
| <div className="p-3 overflow-y-auto h-32 font-mono text-xs"> | |
| {terminalOutput.length === 0 ? ( | |
| <div className="text-gray-500">Terminal output will appear here...</div> | |
| ) : ( | |
| terminalOutput.map((output, index) => ( | |
| <div key={index} className="mb-2"> | |
| <span className="text-gray-400"> | |
| [{output.timestamp.toLocaleTimeString()}] | |
| </span> | |
| <span className={output.type === 'success' ? 'text-green-400' : 'text-red-400'}> | |
| {' '}{output.message} | |
| </span> | |
| </div> | |
| )) | |
| )} | |
| </div> | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </div> | |
| ) | |
| } |