diff --git a/public/app.js b/public/app.js index 437a676..cb11773 100644 --- a/public/app.js +++ b/public/app.js @@ -84,9 +84,7 @@ function renderLiveGrid() { const img = tile.querySelector('img'); if (img) img.src = `/recordings/${cam.name || cam.id}/latest.jpg?t=${Date.now()}`; }, 3000); - tile.addEventListener('click', () => { - window.open(`http://${location.hostname}:1984/stream.html?src=${cam.id}_sub`, '_blank'); - }); + tile.addEventListener('click', () => openFocus(cam)); } else { tile.innerHTML = 'No Camera'; } @@ -98,12 +96,20 @@ function renderLiveGrid() { let focusIdx = -1; function openFocus(cam) { focusIdx = cameras.indexOf(cam); - // Open go2rtc's built-in WebRTC player in a new tab. - // The player provides full live video with sub-second latency. - const url = `http://${location.hostname}:1984/?src=${cam.id}_sub`; - window.open(url, '_blank'); - // Also update the overlay for keyboard nav. + const overlay = document.getElementById('focus-overlay'); + overlay.classList.remove('hidden'); + // Remove any previous iframe. + const oldFrame = overlay.querySelector('iframe.go2rtc'); + if (oldFrame) oldFrame.remove(); + // Embed go2rtc player in iframe — PWA-friendly, no new tab. + const iframe = document.createElement('iframe'); + iframe.className = 'go2rtc'; + iframe.allow = 'autoplay'; + iframe.src = `http://${location.hostname}:1984/stream.html?src=${cam.id}_sub`; + document.getElementById('focus-video').style.display = 'none'; + overlay.appendChild(iframe); document.getElementById('focus-title').textContent = cam.name; + document.getElementById('focus-time').textContent = new Date().toLocaleTimeString() + ' live'; renderThumbs(); } function updateFocus() {} diff --git a/public/style.css b/public/style.css index 52014bd..fa3e045 100644 --- a/public/style.css +++ b/public/style.css @@ -107,6 +107,10 @@ body { #focus-title { flex: 1; font-size: 16px; color: #fff; } .focus-time { font-size: 13px; color: var(--text-muted); } #focus-video { flex: 1; width: 100%; object-fit: contain; } +iframe.go2rtc { + flex: 1; width: 100%; border: none; + position: absolute; inset: 48px 0 0 0; +} .thumb-strip { display: flex; gap: 4px; padding: 8px; background: rgba(0,0,0,.8); overflow-x: auto; @@ -191,11 +195,11 @@ body { /* Grid: 2 columns, auto-height rows */ .grid-4x2 { grid-template-columns: repeat(2, 1fr); - grid-template-rows: auto; + grid-auto-rows: minmax(120px, auto); gap: 4px; height: auto; } - .grid-tile { aspect-ratio: 4/3; } + .grid-tile { aspect-ratio: auto; min-height: 120px; } .tile-label { font-size: 10px; bottom: 3px; left: 4px; padding: 1px 5px; } /* Settings: full-width cards */ @@ -220,7 +224,7 @@ body { @media (max-width: 400px) { /* Single column grid on very small screens */ .grid-4x2 { grid-template-columns: 1fr; } - .grid-tile { aspect-ratio: 16/9; } + .grid-tile { min-height: 160px; } /* Minimal top bar */ .topbar { padding: 6px 8px; }