diff --git a/main.go b/main.go index 68c2e07..19c1eca 100644 --- a/main.go +++ b/main.go @@ -36,7 +36,7 @@ import ( //go:embed static var embeddedStaticFiles embed.FS -const version = "1.0.5" +const version = "1.0.6" // --------------------------------------------------------------------------- // Config structs diff --git a/static/app-common.js b/static/app-common.js index 27e242b..67bb52b 100644 --- a/static/app-common.js +++ b/static/app-common.js @@ -1,7 +1,17 @@ - (function(){ const THEME_KEY = 'force-monitor-theme'; + const FULLSCREEN_INTENT_KEY = 'force-monitor-fullscreen-intent'; + function byId(id){ return id ? document.getElementById(id) : null; } + + function getFullscreenIntent(){ + try { return sessionStorage.getItem(FULLSCREEN_INTENT_KEY) === '1'; } catch (_) { return false; } + } + + function setFullscreenIntent(enabled){ + try { sessionStorage.setItem(FULLSCREEN_INTENT_KEY, enabled ? '1' : '0'); } catch (_) {} + } + function setTheme(theme, opts){ opts = opts || {}; const t = theme === 'light' ? 'light' : 'dark'; @@ -12,6 +22,7 @@ if (typeof opts.onChange === 'function') opts.onChange(t); return t; } + function initTheme(opts){ opts = opts || {}; let theme = 'dark'; @@ -30,21 +41,74 @@ } return theme; } + function updateFullscreenButton(buttonId){ const btn = byId(buttonId || 'fullscreen-toggle'); - if (btn) btn.textContent = document.fullscreenElement ? 'Exit fullscreen' : 'Enter fullscreen'; + if (!btn) return; + if (document.fullscreenElement) { + btn.textContent = 'Exit fullscreen'; + return; + } + btn.textContent = getFullscreenIntent() ? 'Restore fullscreen' : 'Enter fullscreen'; } + + async function requestFullscreenSafe(){ + if (document.fullscreenElement) return true; + if (!document.fullscreenEnabled) return false; + try { + await document.documentElement.requestFullscreen(); + setFullscreenIntent(true); + return true; + } catch (err) { + console.warn('Fullscreen restore/request blocked:', err); + return false; + } + } + async function toggleFullscreen(buttonId){ try { - if (!document.fullscreenElement) await document.documentElement.requestFullscreen(); - else await document.exitFullscreen(); + if (!document.fullscreenElement) { + await requestFullscreenSafe(); + } else { + setFullscreenIntent(false); + await document.exitFullscreen(); + } } catch (err) { console.warn('Fullscreen error:', err); } finally { updateFullscreenButton(buttonId || 'fullscreen-toggle'); } } - function initFullscreen(opts){ + + function bindFullscreenNavPersistence(){ + if (document.documentElement.dataset.fsNavBound) return; + document.documentElement.dataset.fsNavBound = '1'; + + document.addEventListener('click', function(ev){ + const link = ev.target && ev.target.closest ? ev.target.closest('a[href]') : null; + if (!link) return; + const href = link.getAttribute('href') || ''; + const target = link.getAttribute('target') || ''; + if (!href || href.startsWith('#') || target === '_blank' || link.hasAttribute('download')) return; + try { + const url = new URL(link.href, window.location.href); + if (url.origin !== window.location.origin) return; + } catch (_) { + return; + } + if (document.fullscreenElement || getFullscreenIntent()) { + setFullscreenIntent(true); + } + }, true); + + window.addEventListener('pageshow', function(){ + document.querySelectorAll('#fullscreen-toggle, #fullscreen-btn').forEach(function(el){ + updateFullscreenButton(el.id); + }); + }); + } + + async function initFullscreen(opts){ opts = opts || {}; const buttonId = opts.buttonId || 'fullscreen-toggle'; const btn = byId(buttonId); @@ -55,13 +119,23 @@ if (!document.documentElement.dataset.fsListenerBound) { document.documentElement.dataset.fsListenerBound = '1'; document.addEventListener('fullscreenchange', function(){ + setFullscreenIntent(!!document.fullscreenElement); document.querySelectorAll('#fullscreen-toggle, #fullscreen-btn').forEach(function(el){ - el.textContent = document.fullscreenElement ? 'Exit fullscreen' : 'Enter fullscreen'; + updateFullscreenButton(el.id); }); }); } + bindFullscreenNavPersistence(); updateFullscreenButton(buttonId); + + if (getFullscreenIntent() && !document.fullscreenElement) { + // Best effort only: some browsers require a fresh user gesture after navigation. + requestAnimationFrame(function(){ + requestFullscreenSafe().finally(function(){ updateFullscreenButton(buttonId); }); + }); + } } + async function fetchJson(url, opts){ opts = opts || {}; const controller = new AbortController(); @@ -88,5 +162,15 @@ clearTimeout(timer); } } - window.AppUI = { setTheme, initTheme, updateFullscreenButton, toggleFullscreen, initFullscreen, fetchJson }; + + window.AppUI = { + setTheme, + initTheme, + updateFullscreenButton, + toggleFullscreen, + initFullscreen, + fetchJson, + getFullscreenIntent, + setFullscreenIntent + }; })();