Mehdi
Upload components/Editor.jsx with huggingface_hub
82528c0 verified
Raw
History Blame Contribute Delete
12.4 kB
import { useState, useEffect, useRef } from 'react'
import { FiPlay, FiSave, FiShare2, FiSettings, FiDownload, FiUpload, FiTerminal, FiDebug, FiGitPullRequest, FiSearch, FiFile, FiFolder } from 'react-icons/fi'
import dynamic from 'next/dynamic'
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'
import { useHotkeys } from 'react-hotkeys-hook'
const MonacoEditor = dynamic(() => import('@monaco-editor/react'), {
ssr: false,
loading: () => <div className="h-full bg-editor rounded-lg animate-pulse" />
})
export default function Editor() {
const [code, setCode] = useState('')
const [language, setLanguage] = useState('javascript')
const [output, setOutput] = useState('')
const [isRunning, setIsRunning] = useState(false)
const [error, setError] = useState(null)
const [theme, setTheme] = useState('vs-dark')
const [fontSize, setFontSize] = useState(14)
const [showSettings, setShowSettings] = useState(false)
const [savedSnippets, setSavedSnippets] = useState([])
const [snippetName, setSnippetName] = useState('')
const [activeTab, setActiveTab] = useState('editor')
const [terminalOutput, setTerminalOutput] = useState('')
const [debugMode, setDebugMode] = useState(false)
const editorRef = useRef(null)
const languages = [
{ value: 'javascript', label: 'JavaScript', icon: '🟡' },
{ value: 'typescript', label: 'TypeScript', icon: '🔵' },
{ value: 'python', label: 'Python', icon: '🐍' },
{ value: 'java', label: 'Java', icon: '☕' },
{ value: 'csharp', label: 'C#', icon: '🔧' },
{ value: 'go', label: 'Go', icon: '🐹' },
{ value: 'rust', label: 'Rust', icon: '🦀' },
{ value: 'html', label: 'HTML', icon: '📄' },
{ value: 'css', label: 'CSS', icon: '🎨' }
]
const themes = [
{ value: 'vs-dark', label: 'Dark (VSCode)' },
{ value: 'vs-light', label: 'Light (VSCode)' },
{ value: 'hc-black', label: 'High Contrast' }
]
useEffect(() => {
const saved = localStorage.getItem('codeSnippets')
if (saved) {
setSavedSnippets(JSON.parse(saved))
}
}, [])
const handleRunCode = async () => {
if (!code.trim()) return
setIsRunning(true)
setError(null)
setOutput('')
setTerminalOutput('Running code...\n')
try {
const response = await fetch('/api/execute', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ code, language }),
})
const data = await response.json()
if (response.ok) {
setOutput(data.output)
setTerminalOutput(prev => prev + `\nExecution completed successfully.\nOutput:\n${data.output}`)
} else {
throw new Error(data.error || 'Execution failed')
}
} catch (err) {
setError(err.message)
setTerminalOutput(prev => prev + `\nError: ${err.message}`)
} finally {
setIsRunning(false)
}
}
const handleSaveSnippet = () => {
if (!snippetName.trim()) return
const newSnippet = {
id: Date.now(),
name: snippetName,
code,
language,
createdAt: new Date().toISOString()
}
const updatedSnippets = [...savedSnippets, newSnippet]
setSavedSnippets(updatedSnippets)
localStorage.setItem('codeSnippets', JSON.stringify(updatedSnippets))
setSnippetName('')
}
const handleLoadSnippet = (snippet) => {
setCode(snippet.code)
setLanguage(snippet.language)
}
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.${language === 'javascript' ? 'js' : language === 'python' ? 'py' : 'txt'}`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
const handleUploadCode = (e) => {
const file = e.target.files[0]
if (!file) return
const reader = new FileReader()
reader.onload = (event) => {
setCode(event.target.result)
}
reader.readAsText(file)
}
const handleEditorMount = (editor) => {
editorRef.current = editor
}
useHotkeys('ctrl+s', (e) => {
e.preventDefault()
handleSaveSnippet()
})
useHotkeys('f5', (e) => {
e.preventDefault()
handleRunCode()
})
return (
<div className="flex flex-col h-[calc(100vh-64px)]">
<div className="flex items-center justify-between p-2 bg-panel border-b border-border">
<div className="flex items-center space-x-2">
<button
onClick={() => setActiveTab('editor')}
className={`px-3 py-1 rounded text-sm flex items-center space-x-1 ${activeTab === 'editor' ? 'bg-primary text-white' : 'text-gray-400 hover:bg-panel'}`}
>
<FiFile />
<span>Editor</span>
</button>
<button
onClick={() => setActiveTab('terminal')}
className={`px-3 py-1 rounded text-sm flex items-center space-x-1 ${activeTab === 'terminal' ? 'bg-primary text-white' : 'text-gray-400 hover:bg-panel'}`}
>
<FiTerminal />
<span>Terminal</span>
</button>
<button
onClick={() => setActiveTab('debug')}
className={`px-3 py-1 rounded text-sm flex items-center space-x-1 ${activeTab === 'debug' ? 'bg-primary text-white' : 'text-gray-400 hover:bg-panel'}`}
>
<FiDebug />
<span>Debug</span>
</button>
</div>
<div className="flex items-center space-x-2">
<select
value={language}
onChange={(e) => setLanguage(e.target.value)}
className="bg-editor text-white px-3 py-1 rounded text-sm"
>
{languages.map((lang) => (
<option key={lang.value} value={lang.value}>
{lang.icon} {lang.label}
</option>
))}
</select>
<button
onClick={handleRunCode}
disabled={isRunning}
className={`px-3 py-1 rounded text-sm flex items-center space-x-1 ${isRunning ? 'bg-gray-600' : 'bg-success hover:bg-green-600'} text-white transition-colors`}
>
<FiPlay />
<span>{isRunning ? 'Running...' : 'Run (F5)'}</span>
</button>
<button
onClick={() => setShowSettings(!showSettings)}
className="px-3 py-1 bg-panel hover:bg-gray-600 text-white rounded text-sm flex items-center space-x-1 transition-colors"
>
<FiSettings />
<span>Settings</span>
</button>
</div>
</div>
{showSettings && (
<div className="bg-panel rounded-lg p-4 m-2 border border-border">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-300 mb-1">Theme</label>
<select
value={theme}
onChange={(e) => setTheme(e.target.value)}
className="w-full bg-editor text-white px-3 py-2 rounded"
>
{themes.map((t) => (
<option key={t.value} value={t.value}>{t.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-1">Font Size</label>
<input
type="range"
min="12"
max="24"
value={fontSize}
onChange={(e) => setFontSize(parseInt(e.target.value))}
className="w-full"
/>
<div className="text-center text-sm text-gray-300 mt-1">{fontSize}px</div>
</div>
</div>
</div>
)}
<div className="flex-1 overflow-hidden">
{activeTab === 'editor' && (
<div className="h-full bg-editor">
<MonacoEditor
height="100%"
language={language}
theme={theme}
value={code}
onChange={setCode}
onMount={handleEditorMount}
options={{
fontSize,
minimap: { enabled: true },
wordWrap: 'on',
scrollBeyondLastLine: false,
automaticLayout: true,
renderLineHighlight: 'all',
folding: true,
lineNumbers: 'on',
roundedSelection: true,
scrollbar: {
vertical: 'auto',
horizontal: 'auto'
}
/>
</div>
)}
{activeTab === 'terminal' && (
<div className="h-full bg-background p-4 font-mono text-sm text-gray-300 overflow-auto">
<div className="mb-2 flex items-center space-x-2">
<span className="text-green-400">$</span>
<span>Terminal</span>
</div>
<div className="h-[calc(100%-30px)] overflow-auto">
{terminalOutput || 'Terminal output will appear here...'}
</div>
</div>
)}
{activeTab === 'debug' && (
<div className="h-full bg-background p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-gray-300">Debug Console</h3>
<button
onClick={() => setDebugMode(!debugMode)}
className={`px-3 py-1 rounded text-sm ${debugMode ? 'bg-danger' : 'bg-primary'} text-white`}
>
{debugMode ? 'Stop Debugging' : 'Start Debugging'}
</button>
</div>
<div className="h-[calc(100%-50px)] bg-panel rounded p-3 overflow-auto">
{debugMode ? (
<div className="text-gray-300">
<p>Debug mode active. Breakpoints and variables will appear here.</p>
<div className="mt-4 space-y-2">
<div className="flex justify-between">
<span>Status:</span>
<span className="text-green-400">Running</span>
</div>
<div className="flex justify-between">
<span>Breakpoints:</span>
<span>0</span>
</div>
</div>
</div>
) : (
<p className="text-gray-500">Debug console ready. Click "Start Debugging" to begin.</p>
)}
</div>
</div>
)}
</div>
<div className="bg-panel border-t border-border p-2">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<input
type="text"
placeholder="Snippet name"
value={snippetName}
onChange={(e) => setSnippetName(e.target.value)}
className="px-3 py-1 bg-editor text-white rounded text-sm w-48"
/>
<button
onClick={handleSaveSnippet}
disabled={!snippetName.trim()}
className="px-3 py-1 bg-yellow-600 hover:bg-yellow-700 text-white rounded text-sm flex items-center space-x-1 transition-colors disabled:opacity-50"
>
<FiSave />
<span>Save (Ctrl+S)</span>
</button>
</div>
<div className="flex items-center space-x-2">
<label className="px-3 py-1 bg-green-600 hover:bg-green-700 text-white rounded text-sm cursor-pointer flex items-center space-x-1 transition-colors">
<FiUpload />
<span>Upload</span>
<input type="file" className="hidden" onChange={handleUploadCode} accept=".js,.py,.java,.cs,.ts,.go,.rs,.html,.css" />
</label>
<button
onClick={handleDownloadCode}
className="px-3 py-1 bg-blue-600 hover:bg-blue-700 text-white rounded text-sm flex items-center space-x-1 transition-colors"
>
<FiDownload />
<span>Download</span>
</button>
</div>
</div>
</div>
</div>
)
}