const terminal = document.getElementById('terminal'); const titlebar = document.getElementById('titlebar'); const startBtn = document.getElementById('tb-start'); const startMenu = document.getElementById('startmenu'); const tbWinItem = document.getElementById('tb-terminal-item'); const smTermLaunch = document.getElementById('sm-terminal-launch'); const MIN_W = 320, MIN_H = 160; let winState = 'normal'; let savedRect = null; let menuOpen = false; function saveRect() { savedRect = { left: terminal.offsetLeft, top: terminal.offsetTop, width: terminal.offsetWidth, height: terminal.offsetHeight, }; } function setWinState(s) { winState = s; updateTaskbarItem(); } function updateTaskbarItem() { const indicator = tbWinItem.querySelector('.tb-win-indicator'); const closed = winState === 'closed'; tbWinItem.classList.toggle('tb-item-active', winState === 'normal' || winState === 'maximized'); tbWinItem.classList.toggle('tb-item-minimized', winState === 'minimized'); tbWinItem.classList.toggle('tb-item-closed', closed); indicator.style.opacity = closed ? '0' : '1'; } function restoreWindow() { terminal.style.display = ''; terminal.classList.remove('maximized'); if (savedRect) { terminal.style.left = savedRect.left + 'px'; terminal.style.top = savedRect.top + 'px'; terminal.style.width = savedRect.width + 'px'; terminal.style.height = savedRect.height + 'px'; } else { centerWindow(); } setWinState('normal'); } function centerWindow() { terminal.style.left = Math.round(window.innerWidth / 2 - terminal.offsetWidth / 2) + 'px'; terminal.style.top = Math.round((window.innerHeight - 44) / 2 - terminal.offsetHeight / 2) + 'px'; } centerWindow(); updateTaskbarItem(); function toggleMenu(e) { e.stopPropagation(); menuOpen = !menuOpen; startMenu.classList.toggle('open', menuOpen); startMenu.setAttribute('aria-hidden', String(!menuOpen)); startBtn.classList.toggle('active', menuOpen); } startBtn.addEventListener('click', toggleMenu); document.addEventListener('click', (e) => { if (menuOpen && !startMenu.contains(e.target) && e.target !== startBtn) { menuOpen = false; startMenu.classList.remove('open'); startMenu.setAttribute('aria-hidden', 'true'); startBtn.classList.remove('active'); } }); startMenu.querySelectorAll('a').forEach(a => { a.addEventListener('click', () => { menuOpen = false; startMenu.classList.remove('open'); startBtn.classList.remove('active'); }); }); smTermLaunch.addEventListener('click', () => { menuOpen = false; startMenu.classList.remove('open'); startBtn.classList.remove('active'); if (winState === 'closed' || winState === 'minimized') { restoreWindow(); } else if (winState === 'normal' || winState === 'maximized') { terminal.classList.add('flash'); setTimeout(() => terminal.classList.remove('flash'), 300); } }); tbWinItem.addEventListener('click', () => { if (winState === 'closed') { restoreWindow(); } else if (winState === 'minimized') { restoreWindow(); } else if (winState === 'normal' || winState === 'maximized') { saveRect(); terminal.style.display = 'none'; setWinState('minimized'); } }); document.getElementById('btn-close').addEventListener('click', () => { saveRect(); terminal.style.display = 'none'; setWinState('closed'); }); document.getElementById('btn-minimize').addEventListener('click', () => { if (winState === 'minimized') return; if (winState !== 'maximized') saveRect(); terminal.style.display = 'none'; setWinState('minimized'); }); document.getElementById('btn-maximize').addEventListener('click', toggleMaximize); titlebar.addEventListener('dblclick', (e) => { if (e.target.closest('.wm-btn')) return; toggleMaximize(); }); function toggleMaximize() { if (winState === 'maximized') { winState = 'normal'; terminal.classList.remove('maximized'); if (savedRect) { terminal.style.left = savedRect.left + 'px'; terminal.style.top = savedRect.top + 'px'; terminal.style.width = savedRect.width + 'px'; terminal.style.height = savedRect.height + 'px'; } setWinState('normal'); } else { saveRect(); terminal.classList.add('maximized'); terminal.style.left = '0'; terminal.style.top = '0'; terminal.style.width = '100vw'; terminal.style.height = 'calc(100vh - 44px)'; setWinState('maximized'); } } let dragging = false, dragSX, dragSY, dragL, dragT; titlebar.addEventListener('mousedown', (e) => { if (e.target.closest('.wm-btn') || winState === 'maximized') return; dragging = true; dragSX = e.clientX; dragSY = e.clientY; dragL = terminal.offsetLeft; dragT = terminal.offsetTop; titlebar.style.cursor = 'grabbing'; e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (dragging) { terminal.style.left = (dragL + e.clientX - dragSX) + 'px'; terminal.style.top = (dragT + e.clientY - dragSY) + 'px'; } if (resizing) doResize(e.clientX, e.clientY); }); document.addEventListener('mouseup', () => { dragging = false; titlebar.style.cursor = 'grab'; stopResize(); }); let resizing = false, resDir = '', resStart = {}; document.querySelectorAll('.resize-handle').forEach(h => { h.addEventListener('mousedown', (e) => { if (winState === 'maximized') return; resizing = true; resDir = h.dataset.dir; resStart = { mx: e.clientX, my: e.clientY, left: terminal.offsetLeft, top: terminal.offsetTop, width: terminal.offsetWidth, height: terminal.offsetHeight }; document.body.style.cursor = getComputedStyle(h).cursor; e.preventDefault(); }); }); function doResize(mx, my) { const dx = mx - resStart.mx, dy = my - resStart.my; let { left, top, width, height } = resStart; if (resDir.includes('e')) width = Math.max(MIN_W, width + dx); if (resDir.includes('s')) height = Math.max(MIN_H, height + dy); if (resDir.includes('w')) { const nw = Math.max(MIN_W, width-dx); left = left + width - nw; width = nw; } if (resDir.includes('n')) { const nh = Math.max(MIN_H, height-dy); top = top + height - nh; height = nh; } terminal.style.left = left + 'px'; terminal.style.top = top + 'px'; terminal.style.width = width + 'px'; terminal.style.height = height + 'px'; } function stopResize() { if (resizing) { resizing = false; document.body.style.cursor = ''; } } function updateClock() { const now = new Date(); const hh = String(now.getHours()).padStart(2,'0'); const mm = String(now.getMinutes()).padStart(2,'0'); const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']; const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; document.getElementById('tb-time').textContent = `${hh}:${mm}`; document.getElementById('tb-date').textContent = `${days[now.getDay()]} ${String(now.getDate()).padStart(2,'0')} ${months[now.getMonth()]}`; } updateClock(); setInterval(updateClock, 10000);