Optimize Kochmodus: Remove banner, integrate toggle into 3-dots menu, add active border and dual WakeLock/NoSleep fallback
This commit is contained in:
+78
-37
@@ -2333,71 +2333,105 @@ body.modal-open {
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Kochmodus (Keep Screen Awake & Interactive Cooking Experience)
|
||||
Kochmodus (Rahmen um Rezept & Keep Screen Awake)
|
||||
========================================================================== */
|
||||
.recipe-cookmode-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #FFFFFF;
|
||||
border: 1.5px solid rgba(30, 77, 59, 0.22);
|
||||
border-radius: 10px;
|
||||
padding: 11px 16px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 8px rgba(30, 77, 59, 0.06);
|
||||
transition: all 0.25s ease;
|
||||
body.cook-mode-on .recipe-page {
|
||||
border: 3.5px solid var(--tannengruen);
|
||||
box-shadow: 0 0 0 2px rgba(212, 160, 23, 0.45), 0 8px 32px rgba(30, 77, 59, 0.35);
|
||||
position: relative;
|
||||
transition: border 0.25s ease, box-shadow 0.25s ease;
|
||||
}
|
||||
|
||||
body.cook-mode-on .recipe-page::before {
|
||||
content: "👨🍳 Kochmodus aktiv 💡";
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 18px;
|
||||
background: var(--tannengruen);
|
||||
color: var(--creme);
|
||||
font-family: var(--font-hand);
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
padding: 3px 14px;
|
||||
border-radius: 20px;
|
||||
border: 2px solid var(--creme);
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.25);
|
||||
z-index: 15;
|
||||
pointer-events: none;
|
||||
animation: cookmode-badge-pop 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
@keyframes cookmode-badge-pop {
|
||||
0% { transform: scale(0.85); opacity: 0; }
|
||||
100% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
body.cook-mode-on .recipe-page::before {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
font-size: 0.95rem;
|
||||
padding: 2px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile Kebab Kochmodus Row */
|
||||
.cb-mobile-cookmode-item {
|
||||
display: flex !important;
|
||||
justify-content: space-between !important;
|
||||
align-items: center !important;
|
||||
padding: 10px 16px !important;
|
||||
background: rgba(30, 77, 59, 0.05);
|
||||
border-bottom: 1.5px solid rgba(0, 0, 0, 0.08);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
body.cook-mode-on .recipe-cookmode-banner {
|
||||
.cb-mobile-cookmode-item:hover,
|
||||
.cb-mobile-cookmode-item:active {
|
||||
background: rgba(30, 77, 59, 0.12);
|
||||
}
|
||||
|
||||
body.cook-mode-on .cb-mobile-cookmode-item {
|
||||
background: #FFFDF0;
|
||||
border-color: #d4c165;
|
||||
box-shadow: 0 4px 16px rgba(212, 193, 101, 0.28);
|
||||
border-bottom-color: #d4c165;
|
||||
}
|
||||
|
||||
.recipe-cookmode-left {
|
||||
.cookmode-item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.cookmode-icon {
|
||||
font-size: 1.7rem;
|
||||
.cookmode-item-left .cookmode-icon {
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.cookmode-text-group {
|
||||
.cookmode-text-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.cookmode-main-text {
|
||||
font-size: 1.05rem;
|
||||
.cookmode-text-box .cookmode-title {
|
||||
font-family: var(--font-hand);
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
color: var(--tannengruen);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.cookmode-sub-text {
|
||||
font-size: 0.82rem;
|
||||
.cookmode-text-box .cookmode-status-text {
|
||||
font-size: 0.76rem;
|
||||
color: #666;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
body.cook-mode-on .cookmode-sub-text {
|
||||
color: #7a5a00;
|
||||
body.cook-mode-on .cookmode-text-box .cookmode-status-text {
|
||||
color: #8c6800;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.recipe-cookmode-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Custom Schieberegler (Slider Switch) */
|
||||
.cb-switch {
|
||||
position: relative;
|
||||
@@ -2680,6 +2714,13 @@ body.cook-mode-on .cookmode-badge {
|
||||
|
||||
/* Druck-Schutz */
|
||||
@media print {
|
||||
body.cook-mode-on .recipe-page {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
body.cook-mode-on .recipe-page::before {
|
||||
display: none !important;
|
||||
}
|
||||
.recipe-cookmode-banner,
|
||||
.cb-nav-cookmode-toggle,
|
||||
.step-done-check,
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
<!-- Desktop Navigation -->
|
||||
<div class="cb-nav-actions desktop-nav-actions">
|
||||
<div class="cb-nav-cookmode-toggle no-print" title="Kochmodus: Verhindert automatisches Sperren des Bildschirms">
|
||||
<div class="cb-nav-cookmode-toggle no-print" onclick="toggleCookingModeClick(event)" title="Kochmodus: Verhindert automatisches Sperren des Bildschirms" style="cursor: pointer;">
|
||||
<span>👨🍳 Kochmodus</span>
|
||||
<label class="cb-switch cb-switch-sm" for="cook-mode-toggle-nav">
|
||||
<label class="cb-switch cb-switch-sm" onclick="event.stopPropagation()">
|
||||
<input type="checkbox" id="cook-mode-toggle-nav" class="cook-mode-switch-input" onchange="toggleCookingMode(this.checked)">
|
||||
<span class="cb-slider"></span>
|
||||
</label>
|
||||
@@ -52,9 +52,15 @@
|
||||
<span>⋮</span>
|
||||
</button>
|
||||
<div id="cb-mobile-menu" class="cb-mobile-dropdown">
|
||||
<div class="cb-mobile-item" style="display: flex; justify-content: space-between; align-items: center;" onclick="event.stopPropagation();">
|
||||
<span>👨🍳 Kochmodus (Display an)</span>
|
||||
<label class="cb-switch cb-switch-sm" for="cook-mode-toggle-mobile">
|
||||
<div class="cb-mobile-item cb-mobile-cookmode-item" onclick="toggleCookingModeClick(event)" title="Kochmodus: Verhindert automatisches Sperren des Bildschirms">
|
||||
<div class="cookmode-item-left">
|
||||
<span class="cookmode-icon">👨🍳</span>
|
||||
<div class="cookmode-text-box">
|
||||
<span class="cookmode-title">Kochmodus</span>
|
||||
<span class="cookmode-status-text" id="cookmode-status-mobile">Display bleibt an</span>
|
||||
</div>
|
||||
</div>
|
||||
<label class="cb-switch cb-switch-sm" onclick="event.stopPropagation()">
|
||||
<input type="checkbox" id="cook-mode-toggle-mobile" class="cook-mode-switch-input" onchange="toggleCookingMode(this.checked)">
|
||||
<span class="cb-slider"></span>
|
||||
</label>
|
||||
@@ -111,16 +117,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- KOCHMODUS SCHIEBEREGLER BANNER (Mobil, Tablet & Desktop) -->
|
||||
<div class="recipe-cookmode-banner no-print">
|
||||
<div class="recipe-cookmode-left">
|
||||
<span class="cookmode-icon">👨🍳</span>
|
||||
<div class="cookmode-text-group">
|
||||
<span class="cookmode-main-text">Kochmodus</span>
|
||||
<span class="cookmode-sub-text" id="cookmode-sub-text">Verhindert, dass das Display während des Kochens sperrt</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="recipe-cookmode-right">
|
||||
<div class="recipe-cookmode-right">
|
||||
<label class="cb-switch" for="cook-mode-toggle" title="Kochmodus umschalten">
|
||||
<input type="checkbox" id="cook-mode-toggle" class="cook-mode-switch-input" onchange="toggleCookingMode(this.checked)">
|
||||
<span class="cb-slider"></span>
|
||||
@@ -557,36 +554,74 @@
|
||||
});
|
||||
|
||||
// ==========================================
|
||||
// KOCHMODUS: SCREEN WAKE LOCK & DISPLAY WACHHALTEN
|
||||
// KOCHMODUS: SCREEN WAKE LOCK & NOSLEEP VIDEO FALLBACK
|
||||
// ==========================================
|
||||
let wakeLockSentinel = null;
|
||||
let isCookModeActive = false;
|
||||
|
||||
async function requestWakeLock() {
|
||||
// 765-Byte stummes H.264 MP4 als universeller Fallback für iOS Safari / Stromsparmodus
|
||||
const NO_SLEEP_MP4 = "data:video/mp4;base64,AAAAHGZ0eXBtcDQyAAAAAG1wNDJpc29tYXZjMQAAADd2bW92AAAAcG12aGQAAAAA1nEwANZxMADAAMAAA+gAAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAABidHJhawAAAFx0a2hkAAAAAdZxMADWcTAAAAABAAAAAAAAB+gAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACbWRpYQAAACRtaGRyAAAAAAAAAAAAAAAAAAB+gAAA+gB4AAAAAAA1aGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAF8bWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADGR1cmwAAAABAAAARXN0YmwAAABTc3RzZAAAAAAAAAABAAAAR2F2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAHgAeAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//+AAAAAMmF2Y0MBQsAP/v/4AAQcK9AAACWAAAEAAAA8AAAeJAAAAABhdmMxAAAAAAAAAAEAACAAHgAeAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//+AAAAAFHN0dHMAAAAAAAAAAQAAAAEAAAAAAACAAAAAc3RzYwAAAAAAAAABAAAAAQAAAAEAAAABAAAAJHN0c3oAAAAAAAAAAAAAAAEAAABkAAAAAAAQc3RjbwAAAAAAAAABAAAAMAAAADBtZGF0AAAAGBAPAAABuQA1AP/wCAAAAGAAAAAAAAAA==";
|
||||
|
||||
function getNoSleepVideo() {
|
||||
let v = document.getElementById('cookmode-nosleep-video');
|
||||
if (!v) {
|
||||
v = document.createElement('video');
|
||||
v.id = 'cookmode-nosleep-video';
|
||||
v.setAttribute('playsinline', '');
|
||||
v.setAttribute('webkit-playsinline', '');
|
||||
v.setAttribute('loop', '');
|
||||
v.setAttribute('muted', '');
|
||||
v.muted = true;
|
||||
v.style.cssText = 'position:fixed;width:1px;height:1px;top:-100px;left:-100px;opacity:0.001;pointer-events:none;';
|
||||
v.src = NO_SLEEP_MP4;
|
||||
document.body.appendChild(v);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
async function acquireKeepAwake() {
|
||||
let success = false;
|
||||
// 1. Wake Lock API (Standard für moderne Browser)
|
||||
if ('wakeLock' in navigator) {
|
||||
try {
|
||||
wakeLockSentinel = await navigator.wakeLock.request('screen');
|
||||
wakeLockSentinel.addEventListener('release', () => {
|
||||
if (!isCookModeActive) {
|
||||
setCookModeUI(false, true);
|
||||
}
|
||||
wakeLockSentinel = null;
|
||||
});
|
||||
return true;
|
||||
success = true;
|
||||
} catch (err) {
|
||||
console.warn('Wake Lock Anfrage nicht erfolgreich:', err);
|
||||
return false;
|
||||
console.warn('Wake Lock API abgelehnt oder im Stromsparmodus:', err);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
// 2. NoSleep Video Fallback (Garantie für iOS Safari / Stromsparmodus)
|
||||
try {
|
||||
const v = getNoSleepVideo();
|
||||
if (v.paused) {
|
||||
const p = v.play();
|
||||
if (p && typeof p.then === 'function') {
|
||||
await p.catch(() => {});
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
} catch (e) {
|
||||
console.warn('NoSleep Video Fallback Fehler:', e);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
async function releaseWakeLock() {
|
||||
function releaseKeepAwake() {
|
||||
if (wakeLockSentinel !== null) {
|
||||
try {
|
||||
await wakeLockSentinel.release();
|
||||
} catch (e) {}
|
||||
try { wakeLockSentinel.release(); } catch (e) {}
|
||||
wakeLockSentinel = null;
|
||||
}
|
||||
try {
|
||||
const v = document.getElementById('cookmode-nosleep-video');
|
||||
if (v && !v.paused) {
|
||||
v.pause();
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function toggleCookingMode(active) {
|
||||
@@ -595,54 +630,39 @@
|
||||
localStorage.setItem('wtc_cookmode_active', active ? 'true' : 'false');
|
||||
} catch (e) {}
|
||||
|
||||
// Synchronisiere alle Schalter (Desktop-Nav, Banner, Mobil-Menü)
|
||||
// Synchronisiere alle Schalter
|
||||
const switches = document.querySelectorAll('.cook-mode-switch-input');
|
||||
switches.forEach(sw => { sw.checked = active; });
|
||||
|
||||
const statusText = document.getElementById('cookmode-status-mobile');
|
||||
|
||||
if (active) {
|
||||
const supported = await requestWakeLock();
|
||||
setCookModeUI(true, supported);
|
||||
showToast(supported ? '✓ Kochmodus an: Display bleibt an 💡' : '✓ Kochmodus aktiv');
|
||||
await acquireKeepAwake();
|
||||
document.body.classList.add('cook-mode-on');
|
||||
if (statusText) statusText.textContent = 'Aktiv 💡 (Display bleibt an)';
|
||||
showToast('✓ Kochmodus an: Display bleibt an 💡');
|
||||
} else {
|
||||
await releaseWakeLock();
|
||||
setCookModeUI(false, true);
|
||||
releaseKeepAwake();
|
||||
document.body.classList.remove('cook-mode-on');
|
||||
if (statusText) statusText.textContent = 'Display bleibt an';
|
||||
showToast('Kochmodus ausgeschaltet');
|
||||
}
|
||||
}
|
||||
|
||||
function setCookModeUI(active, supported = true) {
|
||||
if (active) {
|
||||
document.body.classList.add('cook-mode-on');
|
||||
} else {
|
||||
document.body.classList.remove('cook-mode-on');
|
||||
}
|
||||
|
||||
const badge = document.getElementById('cook-mode-badge');
|
||||
const subText = document.getElementById('cookmode-sub-text');
|
||||
|
||||
if (badge) {
|
||||
if (active) {
|
||||
badge.textContent = supported ? 'Aktiv 💡' : 'Aktiv';
|
||||
} else {
|
||||
badge.textContent = 'Aus';
|
||||
}
|
||||
}
|
||||
|
||||
if (subText) {
|
||||
if (active) {
|
||||
subText.textContent = supported
|
||||
? 'Display bleibt während des Kochens dauerhaft eingeschaltet 💡'
|
||||
: 'Display-Wachhalten nicht vom Browser unterstützt (Interaktive Schritte aktiv)';
|
||||
} else {
|
||||
subText.textContent = 'Verhindert, dass das Display während des Kochens sperrt';
|
||||
function toggleCookingModeClick(e) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
if (e.target && (e.target.tagName === 'INPUT' || e.target.closest('.cb-switch'))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
toggleCookingMode(!isCookModeActive);
|
||||
}
|
||||
|
||||
// Wenn der Nutzer die App/den Tab wechselt und zurückkommt: Wake Lock erneuern!
|
||||
// Wenn der Nutzer die App/den Tab wechselt und zurückkommt: Wake Lock & Video reaktivieren
|
||||
document.addEventListener('visibilitychange', async () => {
|
||||
if (isCookModeActive && document.visibilityState === 'visible') {
|
||||
await requestWakeLock();
|
||||
await acquireKeepAwake();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -651,11 +671,20 @@
|
||||
try {
|
||||
const saved = localStorage.getItem('wtc_cookmode_active') === 'true';
|
||||
if (saved) {
|
||||
const sw = document.getElementById('cook-mode-toggle');
|
||||
if (sw) {
|
||||
sw.checked = true;
|
||||
toggleCookingMode(true);
|
||||
}
|
||||
const switches = document.querySelectorAll('.cook-mode-switch-input');
|
||||
switches.forEach(sw => { sw.checked = true; });
|
||||
document.body.classList.add('cook-mode-on');
|
||||
isCookModeActive = true;
|
||||
const statusText = document.getElementById('cookmode-status-mobile');
|
||||
if (statusText) statusText.textContent = 'Aktiv 💡 (Display bleibt an)';
|
||||
|
||||
const unlockOnGesture = () => {
|
||||
if (isCookModeActive) acquireKeepAwake();
|
||||
document.removeEventListener('touchstart', unlockOnGesture);
|
||||
document.removeEventListener('click', unlockOnGesture);
|
||||
};
|
||||
document.addEventListener('touchstart', unlockOnGesture, { passive: true });
|
||||
document.addEventListener('click', unlockOnGesture, { passive: true });
|
||||
}
|
||||
} catch (e) {}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user