Optimize Kochmodus: Remove banner, integrate toggle into 3-dots menu, add active border and dual WakeLock/NoSleep fallback

This commit is contained in:
2026-10-25 17:30:00 +01:00
parent 36c9d40dc0
commit d41edb8d0b
2 changed files with 173 additions and 103 deletions
+78 -37
View File
@@ -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,
+95 -66
View File
@@ -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) {}
});