Spaces:
Sleeping
Sleeping
File size: 3,729 Bytes
03a7eb9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | import { useRef, useEffect } from 'react';
import Editor from '@monaco-editor/react';
import { motion } from 'framer-motion';
import { Code2, Loader2, Send } from 'lucide-react';
import clsx from 'clsx';
export default function CodeEditor({
code, onCodeChange,
onRunStep, isRunning, isThinking,
stepCount, isDone,
}) {
const editorRef = useRef(null);
function handleMount(editor) {
editorRef.current = editor;
editor.updateOptions({
fontSize: 13,
lineHeight: 22,
minimap: { enabled: false },
scrollBeyondLastLine: false,
renderLineHighlight: 'gutter',
padding: { top: 12, bottom: 12 },
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
fontLigatures: true,
cursorBlinking: 'smooth',
smoothScrolling: true,
bracketPairColorization: { enabled: true },
});
}
// Auto-resize height based on content
useEffect(() => {
if (editorRef.current) {
editorRef.current.layout();
}
}, [code]);
return (
<div className="glass-card flex flex-col overflow-hidden h-full">
{/* ββ Header βββββββββββββββ */}
<div className="flex items-center justify-between px-4 py-2.5 border-b border-[var(--border-subtle)] bg-[#0D1117]/60">
<div className="flex items-center gap-2">
<Code2 size={14} className="text-emerald-400" />
<span className="text-[10px] font-bold tracking-[0.12em] uppercase text-[var(--text-muted)]">
Code Editor
</span>
{stepCount > 0 && (
<span className="text-[9px] font-mono text-[var(--text-muted)] bg-[var(--bg-elevated)] px-2 py-0.5 rounded">
Step {stepCount}/5
</span>
)}
</div>
<div className="flex items-center gap-2">
{isThinking && (
<motion.div
initial={{ opacity: 0, x: 10 }}
animate={{ opacity: 1, x: 0 }}
className="flex items-center gap-1.5 text-[10px] text-amber-400 font-mono"
>
<Loader2 size={12} className="animate-spin" />
Thinkingβ¦
</motion.div>
)}
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
disabled={isRunning || isDone || !code?.trim()}
onClick={onRunStep}
className={clsx(
'flex items-center gap-1.5 px-3.5 py-1.5 rounded-lg text-[11px] font-bold tracking-wide',
'transition-all duration-200 cursor-pointer',
'disabled:opacity-30 disabled:cursor-not-allowed',
isDone
? 'bg-[var(--bg-elevated)] text-[var(--text-muted)]'
: 'bg-gradient-to-r from-emerald-500 to-emerald-600 text-black hover:shadow-[0_0_16px_rgba(0,255,136,0.3)]'
)}
>
{isRunning ? <Loader2 size={12} className="animate-spin" /> : <Send size={12} />}
{isDone ? 'DONE' : 'RUN STEP'}
</motion.button>
</div>
</div>
{/* ββ Monaco Editor ββββββββ */}
<div className="flex-1 min-h-0">
<Editor
height="100%"
language="python"
theme="vs-dark"
value={code}
onChange={(val) => onCodeChange(val || '')}
onMount={handleMount}
loading={
<div className="flex items-center justify-center h-full gap-2 text-[var(--text-muted)] text-xs">
<Loader2 size={14} className="animate-spin" /> Loading editorβ¦
</div>
}
options={{
readOnly: isRunning,
}}
/>
</div>
</div>
);
}
|