diff --git a/examples/bash-demo.html b/examples/bash-demo.html index 073f98d..6da5304 100644 --- a/examples/bash-demo.html +++ b/examples/bash-demo.html @@ -3,132 +3,47 @@ - Bash Demo - Mini WebContainer + Bash Demo โ€” almostnode agent runtime + -
-
-
-

script.js Bash + Node

- +
+ โ† demos + / + + / + Bash + Node + child_process ยท exec +
+ +
+
+
+ script.js +
+console.log('\nAll commands executed.');
-
-

Preview Ready

- + +
+
+ Preview +
+
+ Ready +
+
+
-
-

Console

-
+ +
+
+ Console +
+
+ diff --git a/examples/demo-ai-chatbot.html b/examples/demo-ai-chatbot.html index 7697fc2..8b2adf6 100644 --- a/examples/demo-ai-chatbot.html +++ b/examples/demo-ai-chatbot.html @@ -1,312 +1,139 @@ - - - - AI Chatbot Demo - Browser Node.js - - - -
-
-

- ๐Ÿค– - AI Chatbot Demo -

-

Next.js + Vercel AI SDK + OpenAI streaming chat

-
+ + + + AI Chatbot Demo โ€” almostnode agent runtime + + + + +
+ โ† demos + / + + / + AI Chatbot + next.js ยท openai ยท streaming +
-
- -
+
+ +
- Preview -
- - + Preview +
+ +
-
-
-
-
-

Starting dev server...

-

This may take a moment

-
+
+
+
+

Starting dev server...

- +
-
- Console -
- - Initializing... +
+ Console +
+
+ Initializing...
-
+
- +
Connected
- - + +
-
+
-
- - + + diff --git a/examples/demo-convex-app.html b/examples/demo-convex-app.html index dc05628..e02d8ed 100644 --- a/examples/demo-convex-app.html +++ b/examples/demo-convex-app.html @@ -1,481 +1,230 @@ - - - - Convex App Demo - Browser Node.js - - - -
-
-

- ๐Ÿ“‹ - Convex App Demo -

-

Next.js + shadcn/ui + Convex running in the browser

-
+ .bottom-panel { + grid-column: 1 / 4; + border-top: 1px solid var(--border); + max-height: 200px; + display: flex; + flex-direction: column; + } + .bottom-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 16px; + border-bottom: 1px solid var(--border); + background: rgba(0,0,0,0.2); + } + .bottom-left { + display: flex; + align-items: center; + gap: 12px; + } + .bottom-right { + display: flex; + align-items: center; + gap: 8px; + } + .deploy-input { + padding: 6px 10px; + font-family: var(--mono); + font-size: 0.7rem; + border: 1px solid var(--border); + background: var(--black); + color: var(--text); + width: 200px; + } + .deploy-input::placeholder { color: var(--text-dim); } + .deploy-input.connected { + width: 140px; + border-color: var(--accent-border); + color: var(--text-dim); + } + .convex-status { + display: none; + align-items: center; + gap: 6px; + padding: 2px 10px; + background: var(--accent-bg); + border: 1px solid var(--accent-border); + font-family: var(--mono); + font-size: 0.6rem; + color: var(--accent-dim); + } + .loading-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex: 1; + font-family: var(--mono); + font-size: 0.72rem; + color: var(--text-dim); + } + .spinner { + width: 32px; + height: 32px; + border: 2px solid var(--border); + border-top-color: var(--accent); + animation: spin 1s linear infinite; + margin-bottom: 12px; + } + @keyframes spin { to { transform: rotate(360deg); } } + @media (max-width: 900px) { + .layout { grid-template-columns: 1fr; } + .file-tree-panel { display: none; } + .editor-panel { border-right: none; } + .bottom-panel { grid-column: 1; } + } + + + +
+ โ† demos + / + + / + Convex App + next.js ยท convex ยท realtime +
-
- -
+
+ +
- Files -
-
- + Files
+
- -
+ +
- Editor - -
-
- + Editor +
+
Select a file to edit
- -
+ +
- Preview -
- - + Preview +
+ +
-
-
-
-
-

Starting dev server...

-

This may take a moment

-
+
+
+
+

Starting dev server...

- +
-
- Console -
- - Initializing... +
+ Console +
+
+ Initializing...
-
- -
+
-
- - + + diff --git a/examples/express-demo.html b/examples/express-demo.html index 6d358dd..b9f26ab 100644 --- a/examples/express-demo.html +++ b/examples/express-demo.html @@ -3,127 +3,42 @@ - Express Demo - Mini WebContainer + Express Demo โ€” almostnode agent runtime + -
-
-
-

server.js Express

- +
+ โ† demos + / + + / + Express Server + express ยท npm install +
+ +
+
+
+ server.js +
-
-

Preview Ready

- + +
+
+ Preview +
+
+ Ready +
+
+
-
-

Console

-
+ +
+
+ Console +
+
+ diff --git a/examples/index.html b/examples/index.html index 297ad05..39dab26 100644 --- a/examples/index.html +++ b/examples/index.html @@ -3,119 +3,42 @@ - Mini WebContainer Demo + HTTP Server โ€” almostnode agent runtime + -
-
-
-

server.js

- +
+ โ† demos + / + + / + HTTP Server + http ยท require() +
+ +
+
+
+ server.js +
-
-

Preview Ready

- + +
+
+ Preview +
+
+ Ready +
+
+
-
-

Console

-
+ +
+
+ Console +
+
+ diff --git a/examples/next-demo.html b/examples/next-demo.html index 374c1cc..6ad2d0c 100644 --- a/examples/next-demo.html +++ b/examples/next-demo.html @@ -3,449 +3,136 @@ - Next.js Demo - WebContainer Test + Next.js Demo โ€” almostnode agent runtime + -

- Next.js Demo - File-based Routing in Browser - - - - - - - - - - - - - - - - - - - - - - Next.js Style - -

-

- File-based routing, API routes, and HMR - all running in your browser -

- -
- - -
- -
+
+ โ† demos + / + + / + Next.js App + next.js ยท app router ยท hmr
- -
HMR Update!
+
HMR Update
-
- +
+
- Editor -
+ Editor +
- Initializing... + Initializing...
+
+ + +
+
- - - - - + + + + +
- -
-
- - + +
+
+ +
-
- Installed: react, react-dom +
+ Installed: react, react-dom
- -
-
- - - + +
+
+ + +
-
- Proxy: Not configured (direct fetch) +
+ Proxy: Not configured
-
+
-
- - +
+ + +
- +
- Live Preview - + Live Preview +
-
- - + +
-
-
N
-
Click "Start Preview" to launch the dev server
-
Uses Service Worker for Next.js-like experience
+ Click Start Preview to launch the Next.js dev server
- - +
- +
- Console - + Console +
-
+
diff --git a/examples/sandbox-next-demo.html b/examples/sandbox-next-demo.html index a3933c0..9b6b351 100644 --- a/examples/sandbox-next-demo.html +++ b/examples/sandbox-next-demo.html @@ -3,298 +3,91 @@ - Sandbox Demo - Secure Cross-Origin Execution + Sandbox Demo โ€” almostnode agent runtime + -

- Sandbox Mode Demo - - - - - Cross-Origin Isolated - -

-

- Code runs in a separate origin iframe - cookies, localStorage, and IndexedDB are fully isolated -

+
+ โ† demos + / + + / + Sandbox Mode + cross-origin ยท isolated +
- How it works: The sandbox runs on localhost:3002 while this page runs on localhost:5173. - Because they're different origins, browser security prevents the sandbox from accessing this page's cookies, localStorage, or IndexedDB. -

- Setup: -
    -
  1. Terminal 1: npm run sandbox (serves sandbox on port 3002)
  2. -
  3. Terminal 2: npm run dev (serves this demo on port 5173)
  4. -
  5. Open this page and click "Connect to Sandbox"
  6. + The sandbox runs on localhost:3002 while this page runs on localhost:5173. + Different origins prevent the sandbox from accessing this page's cookies, localStorage, or IndexedDB. +
      +
    1. Terminal 1: npm run sandbox (port 3002)
    2. +
    3. Terminal 2: npm run dev (port 5173)
    4. +
    5. Click Connect to Sandbox below
-
- +
+
- Code Editor -
+ Code Editor +
- Not connected + Not connected
-
- - +
+
+ +
-
-
-
- - +
+ +
- +
- Execution Result - + Execution Result +
-
-
Click "Connect to Sandbox" to initialize...
+
+
Click Connect to Sandbox to initialize...
- Sandbox iframe: Not connected + Sandbox iframe: Not connected
- +
- Console Output - + Console +
-
+
@@ -381,11 +174,7 @@

function logToResult(message, className = '') { const line = document.createElement('div'); - if (typeof message === 'object') { - line.textContent = JSON.stringify(message, null, 2); - } else { - line.textContent = message; - } + line.textContent = typeof message === 'object' ? JSON.stringify(message, null, 2) : message; if (className) line.className = className; resultOutput.appendChild(line); resultOutput.scrollTop = resultOutput.scrollHeight; @@ -393,84 +182,58 @@

async function initSandbox() { const sandboxUrl = sandboxUrlInput.value.trim(); - if (!sandboxUrl) { - logToConsole('Please enter a sandbox URL', 'error'); - return; - } + if (!sandboxUrl) { logToConsole('Enter a sandbox URL', 'error'); return; } try { setStatus('connecting', 'Connecting...'); - logToConsole('Initializing sandbox at: ' + sandboxUrl); - logToConsole('This page origin: ' + window.location.origin, 'info'); + logToConsole('Initializing sandbox: ' + sandboxUrl); + logToConsole('This origin: ' + window.location.origin, 'info'); - // Create VFS with some test files vfs = new VirtualFS(); vfs.mkdirSync('/app', { recursive: true }); - vfs.writeFileSync('/app/package.json', JSON.stringify({ - name: 'sandbox-test', - version: '1.0.0', - }, null, 2)); + vfs.writeFileSync('/app/package.json', JSON.stringify({ name: 'sandbox-test', version: '1.0.0' }, null, 2)); - // Create runtime with sandbox URL logToConsole('Creating SandboxRuntime...'); runtime = await createRuntime(vfs, { sandbox: sandboxUrl, cwd: '/app', - env: { - NODE_ENV: 'development', - SANDBOX_MODE: 'true', - }, + env: { NODE_ENV: 'development', SANDBOX_MODE: 'true' }, onConsole: (method, args) => { - const message = args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '); - logToConsole(`[sandbox.${method}] ${message}`); + const msg = args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '); + logToConsole(`[sandbox.${method}] ${msg}`); }, }); - // Update UI const sandboxOrigin = new URL(sandboxUrl).origin; - iframeOriginEl.innerHTML = `${sandboxOrigin} (different from ${window.location.origin})`; + iframeOriginEl.innerHTML = `${sandboxOrigin} (isolated from ${window.location.origin})`; setStatus('ready', 'Connected'); - logToConsole('Sandbox connected successfully!', 'success'); - logToConsole('Sandbox origin: ' + sandboxOrigin, 'info'); - logToConsole(''); - logToConsole('Security: The sandbox CANNOT access:', 'info'); - logToConsole(' - Cookies from ' + window.location.origin, 'info'); - logToConsole(' - localStorage from ' + window.location.origin, 'info'); - logToConsole(' - IndexedDB from ' + window.location.origin, 'info'); + logToConsole('Connected', 'success'); + logToConsole(`Sandbox origin: ${sandboxOrigin}`, 'info'); + logToConsole('Sandbox cannot access: cookies, localStorage, IndexedDB from this origin', 'info'); runBtn.disabled = false; initBtn.textContent = 'Reconnect'; - } catch (error) { - setStatus('error', 'Connection failed'); - logToConsole('Failed to connect: ' + error.message, 'error'); + setStatus('error', 'Failed'); + logToConsole('Failed: ' + error.message, 'error'); console.error(error); } } async function runCode() { - if (!runtime) { - logToConsole('Please connect to sandbox first', 'error'); - return; - } - - const code = editorEl.value; + if (!runtime) { logToConsole('Connect first', 'error'); return; } resultOutput.innerHTML = ''; - try { runBtn.disabled = true; runBtn.textContent = 'Running...'; - logToConsole('Executing code in sandbox...'); - - const result = await runtime.execute(code, '/app/main.js'); - - logToConsole('Execution complete!', 'success'); + logToConsole('Executing...'); + const result = await runtime.execute(editorEl.value, '/app/main.js'); + logToConsole('Done', 'success'); logToResult('Exports:', 'success'); logToResult(result.exports); - } catch (error) { - logToConsole('Execution error: ' + error.message, 'error'); + logToConsole('Error: ' + error.message, 'error'); logToResult('Error: ' + error.message, 'error'); console.error(error); } finally { @@ -479,31 +242,16 @@

} } - // Event listeners initBtn.addEventListener('click', initSandbox); runBtn.addEventListener('click', runCode); - - clearResultBtn.addEventListener('click', () => { - resultOutput.innerHTML = ''; - }); - - clearConsoleBtn.addEventListener('click', () => { - consoleOutput.innerHTML = ''; - }); - - // Keyboard shortcut: Ctrl/Cmd + Enter to run + clearResultBtn.addEventListener('click', () => { resultOutput.innerHTML = ''; }); + clearConsoleBtn.addEventListener('click', () => { consoleOutput.innerHTML = ''; }); editorEl.addEventListener('keydown', (e) => { - if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { - e.preventDefault(); - if (!runBtn.disabled) { - runCode(); - } - } + if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); if (!runBtn.disabled) runCode(); } }); - // Auto-connect on page load if sandbox URL is valid logToConsole('Sandbox demo loaded', 'info'); - logToConsole('Click "Connect to Sandbox" to initialize', 'info'); + logToConsole('Click Connect to Sandbox to start', 'info'); diff --git a/examples/shared-styles.css b/examples/shared-styles.css new file mode 100644 index 0000000..91290b3 --- /dev/null +++ b/examples/shared-styles.css @@ -0,0 +1,420 @@ +/* โ”€โ”€ almostnode_manager shared design tokens โ”€โ”€ */ +@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&family=Instrument+Sans:wght@400;500;600;700&display=swap'); + +:root { + --black: #0c0c0c; + --surface: #141414; + --surface-2: #1a1a1a; + --border: #2a2a2a; + --border-bright: #3a3a3a; + --text: #c0c0c0; + --text-bright: #f0f0f0; + --text-dim: #999; + --accent: #00ff88; + --accent-dim: #00cc6a; + --accent-bg: rgba(0, 255, 136, 0.06); + --accent-border: rgba(0, 255, 136, 0.15); + --error: #ff6b6b; + --warn: #e8c872; + --mono: 'IBM Plex Mono', 'Menlo', monospace; + --sans: 'Instrument Sans', system-ui, sans-serif; +} + +* { box-sizing: border-box; margin: 0; padding: 0; } + +body { + font-family: var(--sans); + background: var(--black); + color: var(--text); + min-height: 100vh; + -webkit-font-smoothing: antialiased; +} + +/* Scanline overlay */ +body::after { + content: ''; + position: fixed; + inset: 0; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0,0,0,0.03) 2px, + rgba(0,0,0,0.03) 4px + ); + pointer-events: none; + z-index: 9999; +} + +/* โ”€โ”€ Top bar โ”€โ”€ */ +.demo-topbar { + display: flex; + align-items: center; + gap: 16px; + padding: 0 20px; + height: 44px; + background: rgba(12, 12, 12, 0.92); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border-bottom: 1px solid var(--border); +} +.demo-topbar .logo { + font-family: var(--mono); + font-size: 0.78rem; + font-weight: 600; + color: var(--accent); + text-decoration: none; + letter-spacing: -0.01em; +} +.demo-topbar .sep { + color: var(--border-bright); + font-size: 0.9rem; + user-select: none; +} +.demo-topbar .title { + font-family: var(--mono); + font-size: 0.72rem; + color: var(--text-dim); + text-transform: uppercase; + letter-spacing: 0.06em; +} +.demo-topbar .tag { + font-family: var(--mono); + font-size: 0.6rem; + text-transform: uppercase; + letter-spacing: 0.04em; + padding: 2px 8px; + background: var(--accent-bg); + color: var(--accent-dim); + border: 1px solid var(--accent-border); + margin-left: auto; +} +.demo-topbar .back { + font-family: var(--mono); + font-size: 0.7rem; + color: var(--text-dim); + text-decoration: none; + text-transform: uppercase; + letter-spacing: 0.06em; + transition: color 0.15s; +} +.demo-topbar .back:hover { color: var(--text-bright); } + +/* โ”€โ”€ Panels โ”€โ”€ */ +.panel { + background: var(--surface); + border: 1px solid var(--border); + display: flex; + flex-direction: column; + overflow: hidden; +} +.panel-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px 16px; + border-bottom: 1px solid var(--border); + background: rgba(0,0,0,0.2); +} +.panel-label { + font-family: var(--mono); + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--text-dim); +} + +/* โ”€โ”€ Textarea / editor โ”€โ”€ */ +textarea { + width: 100%; + flex: 1; + padding: 16px; + border: none; + resize: none; + background: transparent; + font-family: var(--mono); + font-size: 0.75rem; + line-height: 1.7; + color: var(--accent); + tab-size: 2; + outline: none; + scrollbar-width: thin; + scrollbar-color: var(--border) transparent; +} +textarea::selection { + background: rgba(0, 255, 136, 0.15); +} + +/* โ”€โ”€ Terminal / output โ”€โ”€ */ +.terminal { + flex: 1; + padding: 12px 16px; + font-family: var(--mono); + font-size: 0.73rem; + line-height: 1.65; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: var(--border) transparent; + min-height: 0; + white-space: pre-wrap; + word-break: break-word; +} +.terminal .t-info { color: var(--text-dim); } +.terminal .t-ok { color: var(--accent); } +.terminal .t-err { color: var(--error); } +.terminal .t-log { color: var(--text); } +.terminal .hmr { color: var(--accent); font-weight: 600; } +.terminal .error { color: var(--error); } +.terminal .warn { color: var(--warn); } +.terminal .success { color: var(--accent); } +.terminal .info { color: var(--text-dim); } + +/* โ”€โ”€ Buttons โ”€โ”€ */ +.btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 7px 18px; + font-family: var(--mono); + font-size: 0.72rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + border: none; + cursor: pointer; + transition: background 0.15s, box-shadow 0.15s; +} +.btn-primary { + background: var(--accent); + color: var(--black); +} +.btn-primary:hover { + background: #00ffaa; + box-shadow: 0 0 16px rgba(0, 255, 136, 0.2); +} +.btn-secondary { + background: var(--surface-2); + color: var(--text); + border: 1px solid var(--border); +} +.btn-secondary:hover { + border-color: var(--text-dim); + color: var(--text-bright); +} +.btn-warn { + background: #3a1a1a; + color: var(--error); + border: 1px solid rgba(255, 107, 107, 0.2); +} +.btn-warn:hover { + background: #4a2020; +} +.btn:disabled { + opacity: 0.4; + cursor: not-allowed; + box-shadow: none; +} + +/* โ”€โ”€ File tabs โ”€โ”€ */ +.file-tabs { + display: flex; + align-items: stretch; + background: var(--black); + border-bottom: 1px solid var(--border); + overflow-x: auto; + scrollbar-width: none; +} +.file-tabs::-webkit-scrollbar { display: none; } +.file-tab { + display: flex; + align-items: center; + gap: 8px; + padding: 9px 16px; + font-family: var(--mono); + font-size: 0.7rem; + color: var(--text-dim); + background: none; + border: none; + border-bottom: 2px solid transparent; + cursor: pointer; + white-space: nowrap; + transition: color 0.15s, border-color 0.15s; +} +.file-tab:hover { color: var(--text); } +.file-tab.active { + color: var(--text-bright); + border-bottom-color: var(--accent); +} +.file-tab .dot { + width: 5px; + height: 5px; + background: var(--text-dim); + border-radius: 50%; + flex-shrink: 0; +} +.file-tab.active .dot { background: var(--accent); } + +/* โ”€โ”€ Status indicator โ”€โ”€ */ +.status-dot { + width: 6px; + height: 6px; + background: var(--border-bright); + border-radius: 50%; + flex-shrink: 0; +} +.status-dot.running { background: var(--accent); } +.status-dot.error { background: var(--error); } + +/* โ”€โ”€ Toolbar row โ”€โ”€ */ +.toolbar { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 16px; + border-top: 1px solid var(--border); + background: var(--black); +} + +/* โ”€โ”€ Preview iframe โ”€โ”€ */ +.preview-frame { + flex: 1; + border: none; + display: block; + background: #0e0e1a; +} +.preview-placeholder { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-family: var(--mono); + font-size: 0.72rem; + color: #444; + text-align: center; + padding: 20px; +} + +/* โ”€โ”€ Input fields โ”€โ”€ */ +input[type="text"] { + padding: 8px 12px; + background: var(--black); + border: 1px solid var(--border); + color: var(--text-bright); + font-family: var(--mono); + font-size: 0.75rem; + outline: none; + transition: border-color 0.15s; +} +input[type="text"]:focus { + border-color: var(--accent-border); +} +input[type="text"]::placeholder { + color: var(--text-dim); +} + +/* โ”€โ”€ HMR indicator โ”€โ”€ */ +.hmr-indicator { + position: fixed; + top: 16px; + right: 16px; + background: var(--accent); + color: var(--black); + padding: 8px 16px; + font-family: var(--mono); + font-size: 0.72rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + opacity: 0; + transform: translateY(-12px); + transition: opacity 0.2s, transform 0.2s; + z-index: 1000; +} +.hmr-indicator.show { + opacity: 1; + transform: translateY(0); +} + +/* โ”€โ”€ URL bar โ”€โ”€ */ +.url-bar { + display: flex; + gap: 0; + padding: 8px 16px; + border-bottom: 1px solid var(--border); + background: rgba(0,0,0,0.2); +} +.url-bar input { + flex: 1; + border-right: none; +} +.url-bar .btn { + padding: 8px 14px; +} + +/* โ”€โ”€ Checkbox toggle โ”€โ”€ */ +.toggle-row { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--mono); + font-size: 0.68rem; + color: var(--text-dim); + cursor: pointer; +} +.toggle-row input[type="checkbox"] { + width: 14px; + height: 14px; + cursor: pointer; + accent-color: var(--accent); +} +.toggle-badge { + padding: 2px 8px; + font-size: 0.6rem; + text-transform: uppercase; + letter-spacing: 0.04em; + background: var(--surface-2); + color: var(--text-dim); + border: 1px solid var(--border); +} +.toggle-badge.on { + background: var(--accent-bg); + color: var(--accent-dim); + border-color: var(--accent-border); +} + +/* โ”€โ”€ Inline section โ”€โ”€ */ +.inline-section { + padding: 10px 16px; + border-bottom: 1px solid var(--border); + background: var(--surface); +} + +/* โ”€โ”€ Router toggle โ”€โ”€ */ +.router-tabs { + display: flex; + gap: 0; + border-bottom: 1px solid var(--border); +} +.router-tab { + flex: 1; + padding: 8px 16px; + font-family: var(--mono); + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-dim); + background: var(--black); + border: none; + border-bottom: 2px solid transparent; + cursor: pointer; + text-align: center; + transition: color 0.15s, border-color 0.15s; +} +.router-tab:hover { color: var(--text); } +.router-tab.active { + color: var(--text-bright); + border-bottom-color: var(--accent); + background: var(--surface); +} diff --git a/examples/vite-demo.html b/examples/vite-demo.html index 7941503..86a03f4 100644 --- a/examples/vite-demo.html +++ b/examples/vite-demo.html @@ -3,305 +3,81 @@ - Vite Demo - WebContainer Test + Vite Demo โ€” almostnode agent runtime + -

๐Ÿš€ Vite Demo - HMR in Browser

-

- Running Vite with Hot Module Replacement using shimmed Node.js APIs -

+
+ โ† demos + / + + / + Vite Dev Server + vite ยท react ยท hmr +
-
๐Ÿ”ฅ HMR Update!
+
HMR Update
-
- +
+
- ๐Ÿ“ Editor -
+ Editor +
- Initializing... + Initializing...
- - - - + + + +
-
+
-
- - - +
+ + +
- +
- ๐Ÿ–ฅ๏ธ Live Preview - + Live Preview +
-
-
๐Ÿš€
-
Click "Start Preview" to launch the dev server
-
Uses Service Worker for fast, Vite-like experience
+ Click Start Preview to launch the dev server
- - +
- +
- ๐Ÿ“‹ Console - + Console +
-
+
@@ -350,13 +126,10 @@

๐Ÿš€ Vite Demo - HMR in Browser

function loadFile(path) { if (!vfs) return; - try { const content = vfs.readFileSync(path, 'utf8'); editorEl.value = content; currentFile = path; - - // Update active tab document.querySelectorAll('.file-tab').forEach((tab) => { tab.classList.toggle('active', tab.dataset.file === path); }); @@ -367,248 +140,119 @@

๐Ÿš€ Vite Demo - HMR in Browser

function saveFile() { if (!vfs || !currentFile) return; - try { vfs.writeFileSync(currentFile, editorEl.value); - log(`๐Ÿ’พ Saved: ${currentFile}`); - - // HMR is now automatically handled by ViteDevServer via file watching - // The postMessage to iframe will receive the update - if (previewActive && !devServer) { - // Fallback for legacy blob URL mode - updatePreview(); - } + log(`Saved: ${currentFile}`); + if (previewActive && !devServer) updatePreview(); } catch (e) { log(`Error saving ${currentFile}: ${e.message}`, 'error'); } } - // Build the preview HTML with all assets inlined (React version) - function buildPreviewHtml() { - if (!vfs) return ''; - - try { - // Read all the source files - const css = vfs.readFileSync('/src/style.css', 'utf8'); - let mainJsx = vfs.readFileSync('/src/main.jsx', 'utf8'); - let appJsx = vfs.readFileSync('/src/App.jsx', 'utf8'); - let counterJsx = vfs.readFileSync('/src/Counter.jsx', 'utf8'); - - // Remove import/export statements - we'll use global React - const cleanJsx = (code) => { - return code - .replace(/^\s*import\s+.*$/gm, '') - .replace(/^\s*export\s+default\s+/gm, '') - .replace(/^\s*export\s+/gm, ''); - }; - - counterJsx = cleanJsx(counterJsx); - appJsx = cleanJsx(appJsx); - mainJsx = cleanJsx(mainJsx); - - // Fix the main.jsx to use our inlined components - mainJsx = mainJsx - .replace('ReactDOM.createRoot', 'window.ReactDOM.createRoot') - .replace('', ''); - - // Note: We break up 'script' to prevent Vite from parsing these as real script tags - const scriptOpen = ''; - - // Build complete HTML with React from CDN - const html = ` - - - - - React + Vite Browser Demo - - ${scriptOpen} src="https://unpkg.com/react@18/umd/react.development.js" crossorigin>${scriptClose} - ${scriptOpen} src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin>${scriptClose} - ${scriptOpen} src="https://unpkg.com/@babel/standalone/babel.min.js">${scriptClose} - - -
- ${scriptOpen} type="text/babel" data-type="module"> - const { useState } = React; - - // === Counter.jsx === - ${counterJsx} - - // === App.jsx === - ${appJsx} - - // === main.jsx === - ${mainJsx} - ${scriptClose} - -`; - - return html; - } catch (e) { - console.error('Error building preview:', e); - return `

Error

${e.message}
`; - } - } - function updatePreview() { if (!previewActive || !devServerUrl) return; - - // Re-register HMR target after reload previewFrame.onload = () => { if (devServer && previewFrame.contentWindow) { devServer.setHMRTarget(previewFrame.contentWindow); } }; - - // Force iframe refresh by adding timestamp - const url = devServerUrl + '?t=' + Date.now(); - previewFrame.src = url; + previewFrame.src = devServerUrl + '?t=' + Date.now(); } async function startPreview() { previewPlaceholder.style.display = 'none'; previewFrame.style.display = 'block'; previewActive = true; - if (devServerUrl) { - // Set up HMR target when iframe loads previewFrame.onload = () => { if (devServer && previewFrame.contentWindow) { devServer.setHMRTarget(previewFrame.contentWindow); - log('๐Ÿ”— HMR target connected to iframe', 'hmr'); + log('HMR target connected', 'hmr'); } }; previewFrame.src = devServerUrl; } - - log('๐Ÿš€ Preview started!'); - log('๐Ÿ’ก Edit a file and save to see HMR in action', 'hmr'); + log('Preview started'); + log('Edit a file and save to trigger HMR', 'hmr'); } - // Initialize async function init() { try { setStatus('', 'Initializing...'); - const result = await initViteDemo(outputEl, null); vfs = result.vfs; runtime = result.runtime; npm = result.npm; - // Set up file watcher for HMR vfs.watch('/src', { recursive: true }, (eventType, filename) => { if (eventType === 'change' && previewActive) { - log(`๐Ÿ“ File ${eventType}: ${filename}`); + log(`File ${eventType}: ${filename}`); } }); - // Enable buttons saveBtn.disabled = false; installBtn.disabled = false; - runBtn.disabled = false; // Start Preview works without npm install now! - - // Load initial file + runBtn.disabled = false; loadFile(currentFile); - setStatus('running', 'Ready'); - log(''); - log('โœ… Demo ready! Click "Start Preview" to launch the dev server.'); - log('๐Ÿ’ก "Install Vite" is optional - the preview uses our built-in dev server.'); + log('Demo ready. Click Start Preview to launch.'); } catch (e) { setStatus('error', 'Error'); - log(`โŒ Initialization error: ${e.message}`, 'error'); + log(`Init error: ${e.message}`, 'error'); console.error(e); } } - // Install Vite async function doInstallVite() { try { installBtn.disabled = true; setStatus('', 'Installing Vite...'); - await installVite(npm, log); - runBtn.disabled = false; setStatus('running', 'Vite installed'); - log(''); - log('โœ… Vite installed! Click "Start Preview" to launch.'); + log('Vite installed. Click Start Preview.'); } catch (e) { setStatus('error', 'Install failed'); - log(`โŒ Install error: ${e.message}`, 'error'); + log(`Install error: ${e.message}`, 'error'); console.error(e); installBtn.disabled = false; } } - // Start Preview using Service Worker-based dev server async function doStartPreview() { try { runBtn.disabled = true; setStatus('', 'Starting dev server...'); - - // Start the dev server with Service Worker - const result = await startDevServer(vfs, { - port: 3000, - log: log, - }); - + const result = await startDevServer(vfs, { port: 3000, log }); devServer = result.server; devServerUrl = result.url; - log(`Dev server URL: ${devServerUrl}`); - - // Listen for HMR updates devServer.on('hmr-update', (update) => { - log(`๐Ÿ”ฅ HMR: ${update.path}`, 'hmr'); + log(`HMR: ${update.path}`, 'hmr'); showHmrIndicator(); }); - - // Start the preview await startPreview(); - setStatus('running', 'Dev server running'); } catch (e) { setStatus('error', 'Preview failed'); - log(`โŒ Preview error: ${e.message}`, 'error'); + log(`Preview error: ${e.message}`, 'error'); console.error(e); runBtn.disabled = false; } } - // Event listeners saveBtn.addEventListener('click', saveFile); - - clearBtn.addEventListener('click', () => { - outputEl.innerHTML = ''; - }); - - refreshBtn.addEventListener('click', () => { - if (previewActive) { - log('โ†ป Refreshing preview...'); - updatePreview(); - } - }); - + clearBtn.addEventListener('click', () => { outputEl.innerHTML = ''; }); + refreshBtn.addEventListener('click', () => { if (previewActive) { log('Refreshing...'); updatePreview(); } }); installBtn.addEventListener('click', doInstallVite); runBtn.addEventListener('click', doStartPreview); - fileTabsEl.addEventListener('click', (e) => { - if (e.target.classList.contains('file-tab')) { - loadFile(e.target.dataset.file); - } + if (e.target.closest('.file-tab')) loadFile(e.target.closest('.file-tab').dataset.file); }); - - // Keyboard shortcut: Ctrl/Cmd + S to save editorEl.addEventListener('keydown', (e) => { - if ((e.ctrlKey || e.metaKey) && e.key === 's') { - e.preventDefault(); - saveFile(); - } + if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); saveFile(); } }); - // Start initialization init();