diff --git a/public/mirror/index.html b/public/mirror/index.html index ef60a19..02e99cf 100644 --- a/public/mirror/index.html +++ b/public/mirror/index.html @@ -22,6 +22,7 @@ refreshTimer: null, renderedAt: 0, ui: {}, + wakeLock: null, showStatus: function (message) { trmnl.ui.img.style.display = "none"; @@ -40,6 +41,8 @@ trmnl.ui.apiKeyInput.value = data.api_key || ""; trmnl.ui.baseURLInput.value = data.base_url || ""; trmnl.ui.displayModeSelect.value = data.display_mode || ""; + trmnl.ui.fullscreenToggle.checked = !!data.fullscreen; + trmnl.ui.wakeLockToggle.checked = !!trmnl.wakeLock || !!data.wake_lock; trmnl.ui.setup.style.display = "flex"; }, @@ -50,6 +53,8 @@ var apiKey = trmnl.ui.apiKeyInput.value; var baseURL = trmnl.ui.baseURLInput.value; var displayMode = trmnl.ui.displayModeSelect.value; + var fullscreenEnabled = trmnl.ui.fullscreenToggle.checked; + var wakeLockEnabled = trmnl.ui.wakeLockToggle.checked; if (!apiKey) { return; @@ -58,9 +63,26 @@ trmnl.saveSettings({ api_key: apiKey, base_url: baseURL, - display_mode: displayMode + display_mode: displayMode, + fullscreen: fullscreenEnabled, + wake_lock: wakeLockEnabled }); + if (wakeLockEnabled) { + trmnl.acquireWakeLock().then(function () { + trmnl.ui.wakeLockToggle.checked = !!trmnl.wakeLock; + }).catch(function (err) { + console.warn("Wake Lock request failed:", err); + trmnl.ui.wakeLockToggle.checked = false; + }); + } else { + trmnl.releaseWakeLock().then(function () { + trmnl.ui.wakeLockToggle.checked = false; + }).catch(function (err) { + console.warn("Wake Lock release failed:", err); + }); + } + trmnl.fetchDisplay(); }, @@ -68,6 +90,144 @@ trmnl.ui.setup.style.display = "none"; }, + isFullscreenSupported: function () { + return !!( + document.fullscreenEnabled || + document.webkitFullscreenEnabled || + document.msFullscreenEnabled + ); + }, + + isFullscreenActive: function () { + return !!( + document.fullscreenElement || + document.webkitFullscreenElement || + document.msFullscreenElement + ); + }, + + enterFullscreen: function () { + if (!trmnl.isFullscreenSupported()) return; + + var el = document.documentElement; + var promise; + + if (el.requestFullscreen) { + promise = el.requestFullscreen(); + } else if (el.webkitRequestFullscreen) { + promise = el.webkitRequestFullscreen(); + } else if (el.msRequestFullscreen) { + promise = el.msRequestFullscreen(); + } + + if (promise && promise.catch) { + promise.catch(function (err) { + console.warn("Enter fullscreen failed:", err); + }); + } + }, + + exitFullscreen: function () { + if (!trmnl.isFullscreenSupported()) return; + if (!trmnl.isFullscreenActive()) return; + + var promise; + + if (document.exitFullscreen) { + promise = document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + promise = document.webkitExitFullscreen(); + } else if (document.msExitFullscreen) { + promise = document.msExitFullscreen(); + } + + if (promise && promise.catch) { + promise.catch(function (err) { + console.warn("Exit fullscreen failed:", err); + }); + } + }, + + syncFullscreenToggle: function () { + var active = trmnl.isFullscreenActive(); + trmnl.ui.fullscreenToggle.checked = active; + }, + + isWakeLockSupported: function () { + return ( + window.isSecureContext && + navigator.wakeLock && + typeof navigator.wakeLock.request === "function" + ); + }, + + acquireWakeLock: function () { + + if (!trmnl.isWakeLockSupported()) { + return { + then: function () { return this; }, + catch: function () { return this; } + }; + } + + if (trmnl.wakeLock) { + return Promise.resolve(); + } + + return navigator.wakeLock.request("screen") + .then(function (sentinel) { + + trmnl.wakeLock = sentinel; + + sentinel.addEventListener("release", function () { + trmnl.wakeLock = null; + trmnl.ui.wakeLockToggle.checked = false; + }); + + console.log("Wake Lock attivo"); + + }) + .catch(function (err) { + console.warn("Wake Lock failed:", err); + trmnl.wakeLock = null; + trmnl.ui.wakeLockToggle.checked = false; + }); + }, + + + releaseWakeLock: function () { + + if (!trmnl.wakeLock) { + return Promise.resolve(); + } + + return trmnl.wakeLock.release() + .then(function () { + trmnl.wakeLock = null; + console.log("Wake Lock rilasciato"); + }) + .catch(function (err) { + console.warn("Release failed:", err); + trmnl.wakeLock = null; + }); + }, + + + toggleWakeLock: function () { + if (!trmnl.isWakeLockSupported()) return; + if (trmnl.wakeLock) { + trmnl.releaseWakeLock().then(function () { + trmnl.ui.wakeLockToggle.checked = false; + }); + } else { + trmnl.acquireWakeLock().then(function () { + trmnl.ui.wakeLockToggle.checked = !!trmnl.wakeLock; + }); + } + }, + + + fetchDisplay: function (opts) { opts = opts || {}; clearTimeout(trmnl.refreshTimer); @@ -132,8 +292,12 @@ trmnl.showStatus("Error processing response: " + e.message); } } else { + var msg = xhr.statusText + if (xhr.status == 404) { + msg = "Maybe wrong API key"; + } trmnl.showStatus( - "Failed to fetch screen: " + xhr.status + " " + xhr.statusText + "Failed to fetch screen: " + xhr.status + " " + msg ); } }; @@ -266,15 +430,110 @@ trmnl.ui.apiKeyInput = document.getElementById("api_key"); trmnl.ui.baseURLInput = document.getElementById("base_url"); trmnl.ui.displayModeSelect = document.getElementById("display_mode"); + trmnl.ui.fullscreenToggle = document.getElementById("fullscreenToggle"); + trmnl.ui.wakeLockToggle = document.getElementById("wakeLockToggle"); trmnl.ui.setup = document.getElementById("setup"); + // Sync fullscreen state + document.addEventListener("fullscreenchange", trmnl.syncFullscreenToggle); + document.addEventListener("webkitfullscreenchange", trmnl.syncFullscreenToggle); + document.addEventListener("msfullscreenchange", trmnl.syncFullscreenToggle); + + // Fullscreen toggle + if (!trmnl.isFullscreenSupported()) { + trmnl.ui.fullscreenToggle.disabled = true; + trmnl.ui.fullscreenToggle.parentElement.style.opacity = "0.5"; + trmnl.ui.fullscreenToggle.parentElement.style.cursor = "not-allowed"; + } else { + trmnl.ui.fullscreenToggle.addEventListener("change", function (e) { + e.stopPropagation(); + + if (e.target.checked) { + trmnl.enterFullscreen(); + } else { + trmnl.exitFullscreen(); + } + }); + } + + var wakeLockHint = document.getElementById("wakeLockHint"); + + // Wake Lock toggle + if (trmnl.isWakeLockSupported()) { + + trmnl.ui.wakeLockToggle.disabled = false; + trmnl.ui.wakeLockToggle.parentElement.style.opacity = "1"; + trmnl.ui.wakeLockToggle.parentElement.style.cursor = "pointer"; + + if (wakeLockHint) wakeLockHint.style.display = "none"; + + trmnl.ui.wakeLockToggle.addEventListener("change", function () { + trmnl.toggleWakeLock(); + }); + + document.addEventListener("visibilitychange", function () { + if ( + document.visibilityState === "visible" && + trmnl.ui.wakeLockToggle.checked + ) { + trmnl.acquireWakeLock(); + } + }); + + } else { + + // unsupported (HTTP or old browser) + trmnl.ui.wakeLockToggle.disabled = true; + trmnl.ui.wakeLockToggle.checked = false; + trmnl.ui.wakeLockToggle.parentElement.style.opacity = "0.5"; + trmnl.ui.wakeLockToggle.parentElement.style.cursor = "not-allowed"; + + if (!window.isSecureContext && wakeLockHint) { + wakeLockHint.style.display = "block"; + } + + } + + // get settings from localstorage var settings = trmnl.getSettings(); + + // show setup form if missing apikey if (!settings || !settings.api_key) { trmnl.showSetupForm(); } else { trmnl.fetchDisplay(); } - } + + // Auto fullscreen at first click/touch if option enabled + if (settings.fullscreen && trmnl.isFullscreenSupported()) { + var activateFullscreenOnce = function () { + trmnl.enterFullscreen(); + document.removeEventListener("click", activateFullscreenOnce); + document.removeEventListener("touchstart", activateFullscreenOnce); + }; + document.addEventListener("click", activateFullscreenOnce, { once: true }); + document.addEventListener("touchstart", activateFullscreenOnce, { once: true }); + } + + // Auto Wake Lock at first click/touch if option enabled + if (settings.wake_lock && trmnl.isWakeLockSupported()) { + var acquireWakeLockOnce = function () { + trmnl.acquireWakeLock().then(function () { + trmnl.ui.wakeLockToggle.checked = !!trmnl.wakeLock; + }).catch(function (err) { + console.warn("Wake Lock request failed:", err); + trmnl.ui.wakeLockToggle.checked = false; + }); + document.removeEventListener("click", acquireWakeLockOnce); + document.removeEventListener("touchstart", acquireWakeLockOnce); + }; + document.addEventListener("click", acquireWakeLockOnce, { once: true }); + document.addEventListener("touchstart", acquireWakeLockOnce, { once: true }); + } + + trmnl.syncFullscreenToggle(); + } //init end + }; document.addEventListener("DOMContentLoaded", function () { @@ -393,8 +652,7 @@ display: block; } - label, - summary { + label { font-size: 1.25em; margin-bottom: 0.5em; cursor: pointer; @@ -423,6 +681,10 @@ width: 100%; } + .btn-secondary { + background-color: #777; + } + .btn-clear { margin-top: 1em; background-color: #777; @@ -447,8 +709,127 @@ background-color: #ffffff; } - #unsupported { - color: red; + .setting-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1em; + flex-wrap: nowrap; + } + + .setting-row label, + .setting-row .toggle-label { + font-size: 1.25em; + margin: 0; + cursor: default; + } + + .setting-row select, + .setting-row .switch { + width: auto; + min-width: 52px; + height: 28px; + } + + .switch { + position: relative; + display: inline-block; + width: 52px; + height: 28px; + } + + .switch input { + opacity: 0; + width: 0; + height: 0; + } + + .slider { + position: absolute; + cursor: pointer; + inset: 0; + background-color: #ccc; + border-radius: 28px; + transition: background-color 0.2s ease; + } + + .slider::before { + content: ""; + position: absolute; + height: 22px; + width: 22px; + left: 3px; + top: 3px; + background-color: white; + border-radius: 50%; + transition: transform 0.2s ease; + } + + .switch input:checked+.slider { + background-color: #f54900; + } + + .switch input:checked+.slider::before { + transform: translateX(24px); + } + + .switch input:disabled+.slider { + background-color: #ccc; + cursor: not-allowed; + } + + .switch input:disabled+.slider::before { + background-color: #eee; + } + + .form-select-small { + width: 6em; + font-size: 1em; + padding: 0.4em 0.5em; + border: 1px solid #ccc; + border-radius: 0.5em; + background-color: #ffffff; + } + + .toggle-label { + font-size: 1.25em; + margin: 0; + cursor: default; + pointer-events: auto; + } + + .setting-hint { + font-size: 0.75em; + color: #f41414; + margin-top: 0.2em; + margin-left: 0.5em; + } + + /* Fallback for iOS 9 */ + @media screen and (max-width: 1024px) and (-webkit-min-device-pixel-ratio: 1) { + .setting-row { + display: block; + overflow: hidden; + } + + .setting-row label, + .setting-row .toggle-label { + float: left; + line-height: 28px; + margin-right: 0.5em; + } + + .setting-row select, + .setting-row .switch { + float: right; + width: auto; + min-width: 52px; + height: 28px; + } + + .setting-hint { + display: none !important; + } } @@ -459,18 +840,6 @@ TRMNL Logo
-
- - -
- -
- -
@@ -478,11 +847,43 @@ class="form-control" value="" />
- +
+ + +
- +
+ + +
+ +
+ Fullscreen + +
+ +
+
+ Screen Wake Lock + +
+ +
+ + +
@@ -496,10 +897,11 @@
- +
+ \ No newline at end of file