diff --git a/ui/components/Artifacts/Content.tsx b/ui/components/Artifacts/Content.tsx index 0b6479c..8303cc6 100644 --- a/ui/components/Artifacts/Content.tsx +++ b/ui/components/Artifacts/Content.tsx @@ -29,8 +29,8 @@ export default function ArtifactContent({ return ; } - if (showLogs) { - return activeArtifactData && ; + if (showLogs && activeArtifactData) { + return ; } return activeArtifact && renderArtifact(artifacts.find(a => a.id === activeArtifact)!); diff --git a/ui/components/Artifacts/Logs.tsx b/ui/components/Artifacts/Logs.tsx index 212ead7..7dc26df 100644 --- a/ui/components/Artifacts/Logs.tsx +++ b/ui/components/Artifacts/Logs.tsx @@ -13,21 +13,15 @@ interface OutputEntry { type: 'log' | 'info' | 'warning' | 'error' | 'success' | 'output' } -export default function Logs() { +interface LogsProps { + entries: OutputEntry[]; +} + +export default function Logs({ entries }: LogsProps) { const outputRef = useRef(null) - const [entries, setEntries] = useState([]) const [isScrolledToBottom, setIsScrolledToBottom] = useState(true) const [isMinimized, setIsMinimized] = useState(false) - const addEntry = useCallback((content: string, type: OutputEntry['type'] = 'output') => { - setEntries(prevEntries => [...prevEntries, { - id: crypto.randomUUID(), - timestamp: new Date().toISOString(), - content, - type, - }]) - }, []) - useEffect(() => { if (outputRef.current && isScrolledToBottom) { outputRef.current.scrollTop = outputRef.current.scrollHeight @@ -39,39 +33,6 @@ export default function Logs() { setIsScrolledToBottom(scrollHeight - scrollTop === clientHeight) }, []) - // Simulating entries for demonstration - useEffect(() => { - const types: OutputEntry['type'][] = ['log', 'info', 'warning', 'error', 'success', 'output'] - const messages = [ - "Initializing quantum neural network...", - "Syncing with decentralized cloud nodes...", - "Optimizing AI-driven microservices...", - "Establishing secure blockchain connection...", - "Deploying edge computing resources...", - "Received request: GET /api/v3/quantum-data", - "Error: Temporal anomaly detected in data stream", - "Successfully processed 1 million records in 0.1 seconds", - "> npm run future", - "$ next quantum-dev", - "- Quantum server ready on 0.0.0.0:3000, multiverse url: http://localhost:3000" - ] - - const addRandomEntry = () => { - const randomType = types[Math.floor(Math.random() * types.length)] - const randomMessage = messages[Math.floor(Math.random() * messages.length)] - addEntry(randomMessage, randomType) - } - - // Add initial entries - for (let i = 0; i < 5; i++) { - addRandomEntry() - } - - const timer = setInterval(addRandomEntry, 2000) - - return () => clearInterval(timer) - }, [addEntry]) - const typeStyles = { error: 'bg-red-500/20 text-red-700 dark:text-red-300 border-red-500/30', warning: 'bg-yellow-500/20 text-yellow-700 dark:text-yellow-300 border-yellow-500/30', @@ -104,8 +65,12 @@ export default function Logs() { >
- {entries.map((entry) => ( - + {entries.map((entry, index) => ( + ))}
diff --git a/ui/components/Artifacts/index.tsx b/ui/components/Artifacts/index.tsx index a07c7dd..c03d0ec 100644 --- a/ui/components/Artifacts/index.tsx +++ b/ui/components/Artifacts/index.tsx @@ -8,6 +8,7 @@ import ArtifactActions from './Actions' import ArtifactSidebar from './Sidebar' import Code from './Code' import TextPreview from './TextPreview' +import usePyodide from '@/hooks/usePyodide' interface ArtifactContent { id: string; @@ -74,6 +75,7 @@ export default function Artifacts({ const [logEntries, setLogEntries] = useState<{[key: string]: OutputEntry[]}>({}); const [copied, setCopied] = useState(false); const { resolvedTheme } = useTheme(); + const { pyodide } = usePyodide(); const activeArtifactData = artifacts.find(a => a.id === activeArtifact); @@ -90,20 +92,60 @@ export default function Artifacts({ } }; - const runCode = () => { + const runCode = async () => { if (activeArtifactData?.type === 'code') { - onRun(activeArtifactData.id, activeArtifactData.content); - setLogEntries(prev => ({ - ...prev, - [activeArtifactData.id]: [ - ...(prev[activeArtifactData.id] || []), - { - timestamp: new Date().toISOString(), - content: `Running ${activeArtifactData.name}...`, - type: 'info' - } - ] - })); + if (activeArtifactData.language === 'python' && pyodide) { + try { + // Capture console output + let output = ''; + const originalConsoleLog = console.log; + console.log = (...args) => { + output += args.join(' ') + '\n'; + originalConsoleLog.apply(console, args); + }; + + const result = await pyodide.runPython(activeArtifactData.content); + + // Restore original console.log + console.log = originalConsoleLog; + + setLogEntries(prev => ({ + ...prev, + [activeArtifactData.id]: [ + ...(prev[activeArtifactData.id] || []), + { + id: crypto.randomUUID(), + timestamp: new Date().toISOString(), + content: output.trim(), + type: 'output' + }, + // Only add the result if it's not undefined + ...(result !== undefined ? [{ + id: crypto.randomUUID(), + timestamp: new Date().toISOString(), + content: `Result: ${result}`, + type: 'output' + }] : []) + ] + })); + } catch (error) { + setLogEntries(prev => ({ + ...prev, + [activeArtifactData.id]: [ + ...(prev[activeArtifactData.id] || []), + { + id: crypto.randomUUID(), + timestamp: new Date().toISOString(), + content: `Error: ${error.message}`, + type: 'error' + } + ] + })); + } + } else { + // Handle other languages or call the original onRun function + onRun(activeArtifactData.id, activeArtifactData.content); + } setShowLogs(true); } }; diff --git a/ui/hooks/usePyodide.ts b/ui/hooks/usePyodide.ts new file mode 100644 index 0000000..2895ded --- /dev/null +++ b/ui/hooks/usePyodide.ts @@ -0,0 +1,39 @@ +import { useState, useEffect } from "react"; + +declare global { + interface Window { + loadPyodide?: () => Promise; + } +} + +const PYODIDE_VERSION = "0.25.0"; + +export default function usePyodide() { + const [pyodide, setPyodide] = useState(null); + + useEffect(() => { + const loadPyodide = async () => { + if (typeof window !== 'undefined' && !window.loadPyodide) { + const script = document.createElement('script'); + script.src = `https://cdn.jsdelivr.net/pyodide/v${PYODIDE_VERSION}/full/pyodide.js`; + script.async = true; + script.onload = async () => { + const loadedPyodide = await (window as any).loadPyodide({ + indexURL: `https://cdn.jsdelivr.net/pyodide/v${PYODIDE_VERSION}/full/`, + }); + setPyodide(loadedPyodide); + }; + document.body.appendChild(script); + } else if (typeof window !== 'undefined' && window.loadPyodide) { + const loadedPyodide = await (window as any).loadPyodide({ + indexURL: `https://cdn.jsdelivr.net/pyodide/v${PYODIDE_VERSION}/full/`, + }); + setPyodide(loadedPyodide); + } + }; + + loadPyodide(); + }, []); + + return { pyodide }; +} \ No newline at end of file