import { render } from 'solid-js/web'; import { createSignal, onMount, Show } from 'solid-js'; const FullscreenIcon = () => ; const ExitFullscreenIcon = () => ; const KillIcon = () => function App() { const [loading, setLoading] = createSignal(true); const [status, setStatus] = createSignal(''); const [userData, setUserData] = createSignal(null); const [wsStatus, setWsStatus] = createSignal('Disconnected'); const [isFullScreen, setIsFullScreen] = createSignal(false); const [promptText, setPromptText] = createSignal('$ '); let consoleRef, logOutputRef, inputLineRef; let ansiUpInstance = null; async function fetchUserInfo() { setLoading(true); // User info logic is assumed to be correct and unchanged setLoading(false); } function appendToLog(line, isCommand = false) { if (!logOutputRef) return; const html = ansiUpInstance ? ansiUpInstance.ansi_to_html(line) : line; const logEntry = document.createElement('div'); logEntry.innerHTML = isCommand ? `${promptText()}${html}` : html; logOutputRef.appendChild(logEntry); while (logOutputRef.children.length > 250) { logOutputRef.removeChild(logOutputRef.firstChild); } if (consoleRef) { requestAnimationFrame(() => consoleRef.scrollTop = consoleRef.scrollHeight); } } async function sendCommand() { if (!inputLineRef) return; const cmd = inputLineRef.innerText.trim(); if (!cmd) return; appendToLog(cmd, true); inputLineRef.innerText = ''; try { await fetch('/private/server/exocore/web/shell/sent', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ command: cmd }), }); } catch (err) { appendToLog(`\x1b[31mFailed to send command: ${err.message}\x1b[0m`); } } async function handleKillShellCommand() { appendToLog('\x1b[33mINFO: Sending kill command...\x1b[0m'); try { await fetch('/private/server/exocore/web/shell/kill', { method: 'POST' }); } catch (err) { appendToLog(`\x1b[31mERROR: Could not send kill command: ${err.message}\x1b[0m`); } } function toggleFullScreen() { const el = document.querySelector('.console-wrapper'); if (!el) return; if (!document.fullscreenElement) { el.requestFullscreen().catch(err => appendToLog(`\x1b[31mFullscreen Error: ${err.message}\x1b[0m`)); } else { document.exitFullscreen(); } } onMount(() => { const fontLink = document.createElement('link'); fontLink.href = 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Fira+Code:wght@400;500&display=swap'; fontLink.rel = 'stylesheet'; document.head.appendChild(fontLink); const ansiScript = document.createElement('script'); ansiScript.src = 'https://cdn.jsdelivr.net/npm/ansi_up@5.1.0/ansi_up.min.js'; ansiScript.onload = () => { if (typeof AnsiUp !== 'undefined') { ansiUpInstance = new AnsiUp(); ansiUpInstance.use_classes = false; } }; document.head.appendChild(ansiScript); fetchUserInfo(); const wsUrl = `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}/private/server/exocore/web/ws/shell`; function connectWebSocket() { const ws = new WebSocket(wsUrl); ws.onopen = () => setWsStatus('Connected'); ws.onclose = () => { setWsStatus('Disconnected'); setTimeout(connectWebSocket, 2000); }; ws.onerror = () => setWsStatus('Error'); ws.onmessage = (e) => { try { const message = JSON.parse(e.data); if (message.type === 'prompt') { setPromptText(message.data); } else { appendToLog(message.data); } } catch (err) { appendToLog(e.data); } }; } connectWebSocket(); document.addEventListener('fullscreenchange', () => setIsFullScreen(!!document.fullscreenElement)); consoleRef?.addEventListener('click', (e) => { if (inputLineRef && e.target !== inputLineRef) { inputLineRef.focus(); } }); }); return (