Spaces:
Paused
Paused
| <html lang="en" style="height: 100%;"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Real Web VM Terminal</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&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: center; | |
| min-height: 100vh; | |
| padding: 1rem; | |
| overflow: hidden; /* Prevent body scroll */ | |
| margin: 0; /* Remove default body margin */ | |
| } | |
| .container { | |
| background-color: #2d3748; /* Slightly lighter dark background */ | |
| padding: 1.5rem; | |
| border-radius: 1rem; | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| max-width: 90%; /* Responsive width */ | |
| width: 800px; /* Max width */ | |
| height: 600px; /* Fixed height for terminal container */ | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| } | |
| h1 { | |
| color: #63b3ed; /* Blue for emphasis */ | |
| margin-bottom: 1rem; | |
| font-size: 1.75rem; | |
| font-weight: 700; | |
| text-align: center; | |
| } | |
| #console-container { | |
| flex-grow: 1; /* Terminal takes remaining space */ | |
| background-color: #000; /* Black background for terminal */ | |
| border-radius: 0.5rem; | |
| overflow: hidden; /* Ensure content fits within */ | |
| border: 1px solid #4a5568; | |
| color: #68d391; /* Green text for terminal output */ | |
| white-space: pre-wrap; /* Preserve whitespace and wrap lines */ | |
| font-family: 'monospace', 'Courier New', Courier, monospace; /* Monospace font for terminal */ | |
| padding: 10px; | |
| text-align: left; /* Ensure text starts from left */ | |
| outline: none; /* Remove outline on focus */ | |
| } | |
| #loading-message { | |
| text-align: center; | |
| color: #ecc94b; /* Yellow for loading */ | |
| margin-top: 1rem; | |
| font-size: 1.1rem; | |
| margin-bottom: 1rem; /* Add margin for spacing */ | |
| } | |
| #error-message { | |
| text-align: center; | |
| color: #f56565; /* Red for error */ | |
| margin-top: 1rem; | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| display: none; /* Hidden by default */ | |
| } | |
| </style> | |
| <!-- CheerpX Library (WebVM core) --> | |
| <!-- This script needs to be loaded before any CheerpX. calls --> | |
| <script src="https://cxrtnc.leaningtech.com/1.1.5/cx.js"></script> | |
| </head> | |
| <body style="height: 100%;"> | |
| <div class="container"> | |
| <h1>Real Web VM Terminal</h1> | |
| <div id="loading-message">Loading Web VM... This may take a moment.</div> | |
| <div id="error-message"></div> | |
| <!-- The <pre> tag is used by CheerpX as the console output --> | |
| <pre id="console-container" tabindex="0"></pre> | |
| </div> | |
| <script type="module"> | |
| // Import CheerpX as a module for modern JS practices | |
| import * as CheerpX from "https://cxrtnc.leaningtech.com/1.1.5/cx.esm.js"; | |
| // Make CheerpX globally available if needed by other scripts, though not strictly necessary here | |
| self.CheerpX = CheerpX; | |
| const consoleContainer = document.getElementById('console-container'); | |
| const loadingMessage = document.getElementById('loading-message'); | |
| const errorMessage = document.getElementById('error-message'); | |
| async function initializeWebVM() { | |
| try { | |
| // Check for Cross-Origin Isolation headers | |
| // This is crucial for SharedArrayBuffer which CheerpX uses. | |
| // If this check fails, it means the page is not served with the necessary headers (COEP/COOP) | |
| // or not over HTTPS, which are required by modern browsers for SharedArrayBuffer. | |
| if (typeof SharedArrayBuffer === 'undefined') { | |
| loadingMessage.style.display = 'none'; // Hide loading message | |
| errorMessage.textContent = 'Error: SharedArrayBuffer is not available. This typically means your page is not served with the required Cross-Origin-Embedder-Policy (COEP) and Cross-Origin-Opener-Policy (COOP) headers, or it is not served over HTTPS. Please serve this file using a local web server (e.g., `python -m http.server`) and access it via `http://localhost:...` or `https://...`.'; | |
| errorMessage.style.display = 'block'; // Show error message | |
| console.error('SharedArrayBuffer not available. Missing COEP/COOP headers or not HTTPS.'); | |
| return; | |
| } | |
| // Create a CloudDevice to load the Linux disk image from Leaning Technologies' CDN | |
| // This is a Debian mini image. | |
| const cloudDevice = await CheerpX.CloudDevice.create( | |
| "wss://disks.webvm.io/debian_mini_20230519_5022088024.ext2" | |
| ); | |
| // Create an IndexedDB device for persistent local storage of changes | |
| // This allows changes made in the VM to persist across browser sessions | |
| const idbDevice = await CheerpX.IDBDevice.create("webvm-disk-storage"); | |
| // Create an OverlayDevice to combine the read-only cloud image with local read-write changes | |
| const overlayDevice = await CheerpX.OverlayDevice.create( | |
| cloudDevice, | |
| idbDevice | |
| ); | |
| // Initialize the Linux environment within CheerpX | |
| const cx = await CheerpX.Linux.create({ | |
| mounts: [ | |
| { type: "ext2", path: "/", dev: overlayDevice }, // Mount the combined disk image as root | |
| { type: "devs", path: "/dev" }, // Standard device files | |
| ], | |
| }); | |
| // Set the console output to our <pre> element | |
| cx.setConsole(consoleContainer); | |
| // Hide loading message and any potential previous error messages | |
| loadingMessage.style.display = 'none'; | |
| errorMessage.style.display = 'none'; | |
| // Focus the console container to enable keyboard input | |
| consoleContainer.focus(); | |
| // Run a full-featured bash shell in the browser | |
| // The --login flag ensures proper shell initialization (e.g., sourcing .bashrc) | |
| await cx.run("/bin/bash", ["--login"], { | |
| env: [ | |
| "HOME=/root", // Set home directory | |
| "USER=root", // Set user | |
| "SHELL=/bin/bash", | |
| "TERM=xterm-256color", // Set terminal type for better compatibility | |
| "EDITOR=vi", | |
| "LANG=en_US.UTF-8", | |
| "LC_ALL=C", | |
| ], | |
| cwd: "/root", // Set current working directory | |
| uid: 0, // Run as root | |
| gid: 0, | |
| }); | |
| } catch (error) { | |
| console.error("Failed to initialize Web VM:", error); | |
| loadingMessage.style.display = 'none'; // Hide loading message | |
| errorMessage.textContent = `Error loading Web VM: ${error.message}. Please check your browser console for more details. Ensure your internet connection is stable as disk images are streamed.`; | |
| errorMessage.style.display = 'block'; // Show error message | |
| errorMessage.style.color = '#f56565'; // Red error | |
| } | |
| } | |
| // Start the Web VM initialization when the window loads | |
| window.onload = initializeWebVM; | |
| </script> | |
| </body> | |
| </html> | |