Welcome

Nexus Server Workspace

Connect to remote servers and manage files, terminals, logs, databases, and containers from one interface.

File Browser
Dual-pane SFTP
SSH Terminal
Full xterm.js
Log Viewer
Real-time tail
Docker
Container manager
Database
MySQL · PG · Redis
Add Server
New connection
Press Ctrl+P to open command palette
File Browser
Local
No folder open
Remote —
SSH Terminal
Log Viewer
nginx/access.log
0 lines
Docker Manager
Containers
Images
Volumes
Networks
4 running · 2 stopped
NameImageStatusPortsCPUMemoryCreatedActions
Database Browser
Ready
Active Server
select a server
Uptime
Resources
CPU42%
Memory6.2/8 GB
Disk34/100 GB
Load0.82, 0.91
Open Sessions
SSH Terminal
SFTP Session
DB Tunnel
Git Status
main ↑2
M nginx.conf
M .env.prod
A deploy.sh
3 uncommitted
Quick Actions
Transfer Queue
Events
Problems 2
0 B/s0 B/s
No problems detected.
SSH Connected SFTP Active
UTF-8 3 servers
// ═══ THEME ═══ function toggleTheme(){ const h=document.documentElement; const dark=h.getAttribute('data-theme')==='dark'; h.setAttribute('data-theme',dark?'light':'dark'); document.getElementById('ico-sun').style.display=dark?'none':'block'; document.getElementById('ico-moon').style.display=dark?'block':'none'; localStorage.setItem('nexus-theme', dark ? 'light' : 'dark'); const isDark=h.getAttribute('data-theme')==='dark'; if(typeof termApplyTheme === 'function') termApplyTheme(); } const DARK_THEME={background:'#0e1117',foreground:'#e4e8f0',cursor:'#6272e4',cursorAccent:'#0e1117',black:'#1a1d24',red:'#e85454',green:'#4caf6e',yellow:'#e8a838',blue:'#6272e4',magenta:'#b57bee',cyan:'#38d4e8',white:'#e4e8f0',brightBlack:'#5f6880',brightRed:'#ff6b6b',brightGreen:'#6fdf8a',brightYellow:'#ffc857',brightBlue:'#7c8df5',brightMagenta:'#cda0ff',brightCyan:'#58eeff',brightWhite:'#ffffff'}; const LIGHT_THEME={background:'#ffffff',foreground:'#1e2030',cursor:'#6272e4',cursorAccent:'#ffffff',black:'#2d3048',red:'#c0392b',green:'#27ae60',yellow:'#d4810a',blue:'#4a5af5',magenta:'#8e44ad',cyan:'#0097a7',white:'#555770',brightBlack:'#888ba8',brightRed:'#e74c3c',brightGreen:'#2ecc71',brightYellow:'#f39c12',brightBlue:'#6272e4',brightMagenta:'#b57bee',brightCyan:'#00bcd4',brightWhite:'#1e2030'}; // Terminal always uses classic green-on-black regardless of UI theme const TERM_THEME={background:'#0a0a0a',foreground:'#33ff33',cursor:'#ff9900',cursorAccent:'#0a0a0a',black:'#0a0a0a',red:'#ff4444',green:'#33ff33',yellow:'#ffaa00',blue:'#4488ff',magenta:'#ff44ff',cyan:'#44ffee',white:'#ccffcc',brightBlack:'#1a3a1a',brightRed:'#ff6666',brightGreen:'#66ff66',brightYellow:'#ffcc44',brightBlue:'#66aaff',brightMagenta:'#ff66ff',brightCyan:'#66ffee',brightWhite:'#eeffee'}; function currentTermTheme(){return TERM_THEME;} // ═══ TABS ═══ const openedTabs=new Set(['welcome']); const tabMeta={ welcome:{label:'Welcome',icon:'',color:'var(--accent)'}, sftp:{label:'File Browser',icon:'',color:'#e85454'}, terminal:{label:'Terminal',icon:'',color:'#4caf6e'}, logs:{label:'nginx.log',icon:'',color:'#e8a838'}, docker:{label:'Docker',icon:'',color:'#38a8e8'}, database:{label:'Database',icon:'',color:'#b57bee'}, }; function openTab(id){ // Close and reopen if switching servers so tab label updates if(openedTabs.has(id)){ const el=document.getElementById('tab-'+id); const srv=el?.querySelector('.tab-srv'); const curSrv=(typeof State!=='undefined'&&State.activeServer)?State.activeServer.name:''; if(srv&&curSrv&&srv.textContent!==curSrv){const t=document.getElementById('tab-'+id);if(t)t.remove();openedTabs.delete(id);} } if(!openedTabs.has(id)){openedTabs.add(id);addTab(id);} switchTab(id); if(typeof State !== 'undefined') State.activePanel = id; sessionStorage.setItem('nx_panel', id); if(id==='terminal')setTimeout(()=>{if(fitAddon)fitAddon.fit();},60); if(id==='logs')startLogStream(); if(id==='docker')renderDocker(); if(id==='database'){renderDbTree();renderQueryResult();} } function addTab(id){ const m=tabMeta[id]||{label:id,icon:'',color:'var(--accent)'}; const t=document.createElement('div'); t.className='tab';t.id='tab-'+id;t.setAttribute('data-tab',id);t.onclick=()=>switchTab(id); const srvName = (typeof State !== 'undefined' && State.activeServer) ? State.activeServer.name : ''; t.innerHTML=`
${m.icon}${m.label}${srvName ? `${srvName}` : ''}`; document.getElementById('tab-bar').insertBefore(t,document.getElementById('tab-bar').lastElementChild); } function switchTab(id){ document.querySelectorAll('.tab').forEach(t=>t.classList.toggle('active',t.getAttribute('data-tab')===id)); document.querySelectorAll('.panel').forEach(p=>p.classList.toggle('active',p.id==='panel-'+id)); } function closeTab(id,e){ e&&e.stopPropagation(); const t=document.getElementById('tab-'+id);if(t)t.remove(); openedTabs.delete(id); const rem=[...openedTabs];if(rem.length)switchTab(rem[rem.length-1]); } // ═══ SIDEBAR ═══ function showSidebar(id,btn){ document.querySelectorAll('.si-btn').forEach(b=>b.classList.remove('active')); document.querySelectorAll('.sidebar-panel').forEach(p=>p.classList.remove('active')); btn.classList.add('active'); document.getElementById('sp-'+id).classList.add('active'); } function toggleGroup(hdr,bodyId){ hdr.classList.toggle('open'); document.getElementById(bodyId).style.display=hdr.classList.contains('open')?'':'none'; } function selectServer(name,el){ document.querySelectorAll('.server-item').forEach(s=>s.classList.remove('selected')); el.classList.add('selected'); document.querySelectorAll('[id^="st-"]').forEach(s=>s.style.display='none'); const st=document.getElementById('st-'+name);if(st)st.style.display=''; } // ═══ TERMINAL ═══ let term,fitAddon; function simulateUpload(){ localUploadSelected(); } function selFile(el, e){ if (e && (e.ctrlKey || e.metaKey)) { el.classList.toggle('sel'); return; } if (e && e.shiftKey) { const items = [...el.closest('.file-list').querySelectorAll('.file-item')]; const lastSel = items.findLastIndex(i => i.classList.contains('sel')); const cur = items.indexOf(el); if (lastSel >= 0) { const [a,b] = [Math.min(lastSel,cur), Math.max(lastSel,cur)]; items.slice(a, b+1).forEach(i => i.classList.add('sel')); return; } } el.closest('.file-list').querySelectorAll('.file-item').forEach(f=>f.classList.remove('sel')); el.classList.add('sel'); } function renderFiles(){} // ═══ EVENTS ═══ function renderEvents(){} function renderTransfers(){} // ═══ COMMAND PALETTE ═══ const PAL_CMDS=[ {cat:'Actions',label:'Open File Browser',sub:'SFTP dual-pane',fn:()=>openTab('sftp')}, {cat:'Actions',label:'Open Terminal',sub:'SSH terminal',fn:()=>openTab('terminal'),kbd:'⌘T'}, {cat:'Actions',label:'Open Log Viewer',sub:'Tail remote logs',fn:()=>openTab('logs')}, {cat:'Actions',label:'Open Docker Manager',sub:'Container control',fn:()=>openTab('docker')}, {cat:'Actions',label:'Open Database Browser',sub:'MySQL · PG · Redis',fn:()=>openTab('database')}, {cat:'Actions',label:'Connect New Server',sub:'Add SSH/SFTP connection',fn:()=>openModal('modal-connect')}, // server entries populated dynamically {cat:'Tools',label:'Reload nginx',sub:'sudo nginx -s reload',fn:()=>showToast('nginx reloaded successfully','success')}, {cat:'Tools',label:'Toggle Dark/Light Mode',sub:'Switch theme',fn:toggleTheme}, {cat:'Tools',label:'Clear Transfer Queue',sub:'Remove completed',fn:()=>{const p=document.getElementById('btm-xfr');if(p)p.innerHTML='';showToast('Queue cleared','info');}}, ]; let palSel=0; function openPalette(){document.getElementById('palette').classList.add('open');document.getElementById('pal-input').value='';filterPalette('');setTimeout(()=>document.getElementById('pal-input').focus(),50);} function closePalette(e){if(e&&e.target!==document.getElementById('palette'))return;document.getElementById('palette').classList.remove('open');} function filterPalette(q){ const res=PAL_CMDS.filter(c=>!q||c.label.toLowerCase().includes(q.toLowerCase())||c.sub.toLowerCase().includes(q.toLowerCase())); const cats=[...new Set(res.map(r=>r.cat))];palSel=0; document.getElementById('pal-results').innerHTML=cats.map((cat,ci)=>`
${cat}
${res.filter(r=>r.cat===cat).map((r,i)=>`
${r.label}${r.sub}${r.kbd?`${r.kbd}`:''}
`).join('')}`).join(''); } function execPal(i){document.getElementById('palette').classList.remove('open');PAL_CMDS[i]?.fn();} function palKey(e){ const items=document.querySelectorAll('.pal-result'); if(e.key==='ArrowDown')palSel=Math.min(palSel+1,items.length-1); else if(e.key==='ArrowUp')palSel=Math.max(palSel-1,0); else if(e.key==='Enter'){items[palSel]?.click();return;} else if(e.key==='Escape'){document.getElementById('palette').classList.remove('open');return;} items.forEach((el,i)=>el.classList.toggle('sel',i===palSel)); items[palSel]?.scrollIntoView({block:'nearest'}); } document.addEventListener('keydown',e=>{if((e.ctrlKey||e.metaKey)&&e.key==='p'){e.preventDefault();openPalette();}}); // ═══ MODAL ═══ function openModal(id){document.getElementById(id).classList.add('open');} function closeModal(e){if(e.target.classList.contains('modal-overlay'))e.target.classList.remove('open');} function closeModalById(id){document.getElementById(id).classList.remove('open');} function toggleAuth(v){document.getElementById('auth-key-field').style.display=v==='password'?'none':'';document.getElementById('auth-pass-field').style.display=v==='password'?'':'none';} function pickColor(el){el.closest('.color-pick').querySelectorAll('.cswatch').forEach(s=>s.classList.remove('active'));el.classList.add('active');} function saveServer(){showToast('Server added & connecting…','success');closeModalById('modal-connect');} // ═══ TOASTS ═══ function showToast(msg,type='info'){ const icons={success:'',error:'',warn:'',info:''}; const t=document.createElement('div');t.className=`toast ${type}`; t.innerHTML=`${icons[type]||icons.info}${msg}`; document.getElementById('toast-cnt').appendChild(t); setTimeout(()=>{t.style.opacity='0';},2700); setTimeout(()=>t.remove(),3100); } // ═══ BOTTOM TABS ═══ function switchBtmTab(el,id){ document.querySelectorAll('.btm-tab').forEach(t=>t.classList.remove('active'));el.classList.add('active'); document.querySelectorAll('.btm-pane').forEach(p=>p.classList.remove('active')); document.getElementById(id==='xfr'?'btm-xfr':id==='events'?'btm-events':'btm-probs').classList.add('active'); } function switchSubTab(el){el.closest('.sub-tabs').querySelectorAll('.sub-tab').forEach(t=>t.classList.remove('active'));el.classList.add('active');} // ═══ RESOURCES ═══ function updateResources(){ const cpu=Math.floor(Math.random()*55)+20;const mem=Math.floor(Math.random()*20)+65; document.getElementById('ctx-cpu').textContent=cpu+'%'; document.getElementById('m-cpu').style.width=cpu+'%'; document.getElementById('m-cpu').className='mfill'+(cpu>80?' danger':cpu>60?' warn':''); document.getElementById('ctx-mem').textContent=(mem/10).toFixed(1)+'/8 GB'; document.getElementById('m-mem').style.width=mem+'%'; document.getElementById('m-mem').className='mfill'+(mem>85?' danger':' warn'); document.getElementById('ctx-load').textContent=(Math.random()*1.5).toFixed(2)+', '+(Math.random()*1.2).toFixed(2); document.getElementById('tx-rate').textContent=fmtB(Math.floor(Math.random()*8000))+'/s'; document.getElementById('rx-rate').textContent=fmtB(Math.floor(Math.random()*24000))+'/s'; } function fmtB(b){return b<1024?b+'B':b<1048576?(b/1024).toFixed(1)+'KB':(b/1048576).toFixed(1)+'MB';} // ═══ GLOBAL SEARCH ═══ function doGlobalSearch(q){ const el=document.getElementById('global-results'); if(!q){el.textContent='Type to search all servers';return;} const srvName=(typeof State!=='undefined'&&State.activeServer)?State.activeServer.name:'server'; const items=containers.filter(c=>c.name.includes(q)).map(c=>({label:c.name,sub:'Docker on '+srvName})); el.innerHTML=items.length?items.map(it=>'
'+it.label+'
'+it.sub+'
').join(''):'No results for "'+q+'"'; } // ═══ TASKS PANEL ═══ function renderTasks(){ const tasks=[]; document.getElementById('tasks-list').innerHTML=tasks.map(t=>`
${t.icon}
${t.label}
${t.srv}
${t.st}
`).join(''); } // ═══ CLOCK ═══ setInterval(()=>{document.getElementById('sb-time').textContent=new Date().toLocaleTimeString();},1000); // ═══ INIT ═══ document.addEventListener('DOMContentLoaded',()=>{ // Apply saved theme preference const savedTheme = localStorage.getItem('nexus-theme') || 'light'; document.documentElement.setAttribute('data-theme', savedTheme); const isLight = savedTheme === 'light'; const sun = document.getElementById('ico-sun'); const moon = document.getElementById('ico-moon'); if (sun) sun.style.display = isLight ? 'none' : 'block'; if (moon) moon.style.display = isLight ? 'block' : 'none'; if (typeof localRender === 'function') localRender(); renderTransfers(); renderEvents(); renderTasks(); updateResources(); document.getElementById('sb-time').textContent=new Date().toLocaleTimeString(); setInterval(updateResources,3000); // animate transfer let p=64;const ti=setInterval(()=>{p+=Math.random()*3;if(p>=100){clearInterval(ti);p=100;} const el=document.getElementById('xfr-progress'); if(el) el.style.width=p+'%'; },400); // nexus.js handles boot + login });