Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Interactive Python Executor</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet"> | |
| <style> | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: #1a202c; /* Dark background */ | |
| color: #e2e8f0; /* Light text */ | |
| display: flex; | |
| justify-content: center; | |
| align-items: flex-start; | |
| min-height: 100vh; | |
| padding: 2rem; | |
| box-sizing: border-box; | |
| } | |
| .container { | |
| background-color: #2d3748; /* Slightly lighter dark for container */ | |
| border-radius: 0.75rem; /* rounded-xl */ | |
| box-shadow: 0 10px 15px rgba(0, 0, 0, 0.2); /* shadow-xl */ | |
| padding: 2rem; | |
| max-width: 900px; | |
| width: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1.5rem; | |
| } | |
| textarea, input[type="text"] { | |
| background-color: #1a202c; /* Even darker for input fields */ | |
| color: #e2e8f0; | |
| border: 1px solid #4a5568; /* border-gray-600 */ | |
| border-radius: 0.5rem; /* rounded-lg */ | |
| padding: 0.75rem 1rem; | |
| font-family: 'Fira Code', 'Cascadia Code', 'Consolas', monospace; /* Monospace for code */ | |
| font-size: 0.9rem; | |
| resize: vertical; | |
| } | |
| button { | |
| padding: 0.75rem 1.5rem; | |
| border-radius: 0.5rem; /* rounded-lg */ | |
| font-weight: 600; /* font-semibold */ | |
| transition: background-color 0.2s, transform 0.1s; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| } | |
| button:hover { | |
| transform: translateY(-1px); | |
| } | |
| .btn-primary { | |
| background-color: #4299e1; /* blue-500 */ | |
| color: white; | |
| } | |
| .btn-primary:hover { | |
| background-color: #3182ce; /* blue-600 */ | |
| } | |
| .btn-secondary { | |
| background-color: #a0aec0; /* gray-400 */ | |
| color: #2d3748; | |
| } | |
| .btn-secondary:hover { | |
| background-color: #718096; /* gray-500 */ | |
| } | |
| .terminal-output { | |
| background-color: #000; /* Black for terminal */ | |
| color: #0f0; /* Green text for terminal */ | |
| border-radius: 0.5rem; | |
| padding: 1rem; | |
| min-height: 200px; | |
| max-height: 400px; | |
| overflow-y: auto; | |
| white-space: pre-wrap; /* Preserve whitespace and wrap text */ | |
| font-family: 'Fira Code', 'Cascadia Code', 'Consolas', monospace; | |
| font-size: 0.85rem; | |
| border: 1px solid #4a5568; | |
| } | |
| .input-area { | |
| display: flex; | |
| gap: 0.5rem; | |
| margin-top: 1rem; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| .status-message { | |
| font-size: 0.8rem; | |
| color: #cbd5e0; | |
| margin-top: 0.5rem; | |
| } | |
| </style> | |
| </head> | |
| <body class="antialiased"> | |
| <div class="container"> | |
| <h1 class="text-3xl font-bold text-center mb-4 text-blue-300">Interactive Python Interpreter</h1> | |
| <p class="text-center text-gray-400 mb-6">Enter Python code and interact with it in real-time.</p> | |
| <div class="flex flex-col gap-4"> | |
| <label for="code-input" class="text-lg font-semibold text-gray-200">Python Code:</label> | |
| <textarea id="code-input" rows="15" class="focus:ring-blue-500 focus:border-blue-500"> | |
| # Example: Interactive input | |
| n = int(input("Enter a number: ")) | |
| if n % 2 == 0: | |
| print("Even") | |
| else: | |
| print("Odd") | |
| # Example: Multiple inputs | |
| name = input("What is your name? ") | |
| age = int(input("How old are you? ")) | |
| print(f"Hello, {name}! You are {age} years old.") | |
| </textarea> | |
| <button id="run-button" class="btn-primary">Run Code</button> | |
| </div> | |
| <div class="flex flex-col gap-4"> | |
| <label for="terminal-output" class="text-lg font-semibold text-gray-200">Terminal Output:</label> | |
| <div id="terminal-output" class="terminal-output"></div> | |
| <div id="input-prompt-area" class="input-area hidden"> | |
| <input type="text" id="user-input" class="flex-grow" placeholder="Enter input here..."> | |
| <button id="send-input-button" class="btn-primary">Send Input</button> | |
| </div> | |
| <p id="status-message" class="status-message">Status: Not connected</p> | |
| </div> | |
| </div> | |
| <script> | |
| const codeInput = document.getElementById('code-input'); | |
| const runButton = document.getElementById('run-button'); | |
| const terminalOutput = document.getElementById('terminal-output'); | |
| const inputPromptArea = document.getElementById('input-prompt-area'); | |
| const userInput = document.getElementById('user-input'); | |
| const sendInputButton = document.getElementById('send-input-button'); | |
| const statusMessage = document.getElementById('status-message'); | |
| let ws; | |
| let isAwaitingInput = false; | |
| function connectWebSocket() { | |
| let wsUrl; | |
| if (window.location.protocol === 'https:') { | |
| // For Hugging Face Spaces, always use wss:// when the page is loaded over HTTPS | |
| // window.location.hostname is safer as it never includes the port | |
| wsUrl = `wss://${window.location.hostname}/ws`; | |
| } else { | |
| // For local development (http://localhost:7860) | |
| wsUrl = `ws://${window.location.host}/ws`; | |
| } | |
| console.log('Attempting to connect WebSocket to:', wsUrl); | |
| console.log('Current page protocol:', window.location.protocol); | |
| console.log('Current page hostname:', window.location.hostname); | |
| console.log('Current page host (includes port):', window.location.host); | |
| ws = new WebSocket(wsUrl); | |
| ws.onopen = () => { | |
| statusMessage.textContent = 'Status: Connected'; | |
| runButton.disabled = false; | |
| console.log('WebSocket connected'); | |
| }; | |
| ws.onmessage = (event) => { | |
| const data = JSON.parse(event.data); | |
| if (data.type === 'output') { | |
| terminalOutput.textContent += data.content; | |
| terminalOutput.scrollTop = terminalOutput.scrollHeight; // Auto-scroll | |
| } else if (data.type === 'input_request') { | |
| isAwaitingInput = true; | |
| inputPromptArea.classList.remove('hidden'); | |
| userInput.focus(); | |
| terminalOutput.scrollTop = terminalOutput.scrollHeight; // Auto-scroll | |
| } else if (data.type === 'execution_complete') { | |
| isAwaitingInput = false; | |
| inputPromptArea.classList.add('hidden'); | |
| userInput.value = ''; | |
| statusMessage.textContent = 'Status: Execution complete.'; | |
| console.log('Execution complete:', data.result); | |
| } else if (data.type === 'pong') { | |
| // console.log('Pong received'); | |
| } | |
| }; | |
| ws.onclose = (event) => { | |
| statusMessage.textContent = `Status: Disconnected (Code: ${event.code}, Reason: ${event.reason})`; | |
| runButton.disabled = true; | |
| inputPromptArea.classList.add('hidden'); | |
| console.log('WebSocket disconnected:', event); | |
| // Attempt to reconnect after a delay | |
| setTimeout(connectWebSocket, 3000); | |
| }; | |
| ws.onerror = (error) => { | |
| statusMessage.textContent = 'Status: Connection error'; | |
| console.error('WebSocket error:', error); | |
| }; | |
| } | |
| runButton.addEventListener('click', () => { | |
| if (ws && ws.readyState === WebSocket.OPEN) { | |
| terminalOutput.textContent = ''; // Clear previous output | |
| statusMessage.textContent = 'Status: Running code...'; | |
| isAwaitingInput = false; | |
| inputPromptArea.classList.add('hidden'); // Hide input area initially | |
| userInput.value = ''; // Clear input field | |
| const code = codeInput.value; | |
| ws.send(JSON.stringify({ type: 'code', content: code })); | |
| } else { | |
| statusMessage.textContent = 'Status: Not connected. Please wait or refresh.'; | |
| } | |
| }); | |
| sendInputButton.addEventListener('click', () => { | |
| if (isAwaitingInput && ws && ws.readyState === WebSocket.OPEN) { | |
| const inputContent = userInput.value; | |
| ws.send(JSON.stringify({ type: 'input', content: inputContent })); | |
| userInput.value = ''; // Clear input field after sending | |
| inputPromptArea.classList.add('hidden'); // Hide input area until next prompt | |
| isAwaitingInput = false; | |
| } | |
| }); | |
| userInput.addEventListener('keypress', (event) => { | |
| if (event.key === 'Enter') { | |
| sendInputButton.click(); | |
| } | |
| }); | |
| // Start WebSocket connection when the page loads | |
| document.addEventListener('DOMContentLoaded', connectWebSocket); | |
| // Optional: Send a ping every 30 seconds to keep the WebSocket connection alive | |
| setInterval(() => { | |
| if (ws && ws.readyState === WebSocket.OPEN) { | |
| ws.send(JSON.stringify({ type: 'ping' })); | |
| } | |
| }, 30000); // 30 seconds | |
| </script> | |
| </body> | |
| </html> | |