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