Last active
October 9, 2025 15:00
-
-
Save nealrs/f0538c02c90cf90f3e993478572a650c to your computer and use it in GitHub Desktop.
wikijs snippet to add recipe mode (wakelock) toggle to all pages under the /recipe/ path
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script> | |
| // SCROLL CHECK TO INSERT WEBCOMPONENT | |
| function scrollThresholdCheck() { | |
| const SCROLL_THRESHOLD = 30; // Scroll distance in pixels to trigger the component insertion | |
| const WEB_COMP = 'recipe-mode-toggle'; | |
| const currentScrollY = window.scrollY; | |
| // Check 1: Has the required scroll threshold been met? | |
| if (currentScrollY >= SCROLL_THRESHOLD) { | |
| // Check 2: Does the target element already exist on the page? | |
| const existingComponent = document.querySelector( WEB_COMP); | |
| if (!existingComponent) { | |
| // Component insertion logic | |
| const target = document.querySelectorAll('div.contents')[0]; | |
| const newComponent = document.createElement( WEB_COMP); | |
| target.prepend(newComponent); | |
| console.debug(`[DOM Update] <${ WEB_COMP}> added to <${target}>.`); | |
| // Optimization: Remove the listener once the component has been successfully added. | |
| window.removeEventListener('scroll', scrollThresholdCheck); | |
| } | |
| } | |
| } | |
| // DOM LISTENER METHOD TO SETUP SCROLL CHECK | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // This function will be executed as soon as the DOM is ready. | |
| console.debug('DOM fully loaded & parsed.'); | |
| // Check if this page's URL contains the path "/recipes/" | |
| //if (window.location.pathname.includes('/recipes/')) { // too naive. | |
| if(/recipes\/.*\//.test(window.location.pathname)){ // i didn't know you could run regex checks like this! | |
| console.info(`YES, this is a recipe page.`); | |
| window.addEventListener('scroll', scrollThresholdCheck); | |
| } | |
| }); | |
| // WAKE LOCK TOGGLE WEB COMPONENT | |
| let wakeLock = null; | |
| class RecipeModeToggle extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.wakeLockSentinel = null; | |
| this.isWakeLockActive = false; | |
| this.attachShadow({ mode: 'open' }); | |
| } | |
| connectedCallback() { | |
| if ('wakeLock' in navigator) { | |
| this.render(); | |
| this.attachEventListeners(); | |
| } else { | |
| console.error('Wake Lock API is not supported by this browser.'); | |
| this.style.display = 'none'; // Hide the component | |
| } | |
| } | |
| render() { | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| #recipe-mode-btn { | |
| font-family: inherit; | |
| font-size: 1rem; | |
| font-weight: 500; | |
| color: #fff; | |
| background-color: #007bff; /* Blue for "Enable" */ | |
| border: none; | |
| padding: 12px 24px; | |
| margin-bottom: 10px; | |
| border-radius: .5rem; | |
| cursor: pointer; | |
| min-width: 200px; | |
| transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.1s ease; | |
| user-select: none; | |
| } | |
| #recipe-mode-btn:hover { | |
| background-color: #0056b3; | |
| } | |
| #recipe-mode-btn:active { | |
| transform: scale(0.98); | |
| } | |
| /* Style for the "on" state */ | |
| #recipe-mode-btn.active { | |
| background-color: #dc3545; /* Red for "Disable" */ | |
| box-shadow: 0 0 15px rgba(220, 53, 69, 0.5); | |
| } | |
| #recipe-mode-btn.active:hover { | |
| background-color: #c82333; | |
| } | |
| </style> | |
| <div><button id="recipe-mode-btn">Recipe Mode OFF</button></div> | |
| `; | |
| } | |
| attachEventListeners() { | |
| const button = this.shadowRoot.querySelector('#recipe-mode-btn'); | |
| button.addEventListener('click', () => this.toggleWakeLock()); | |
| } | |
| async toggleWakeLock() { | |
| try { | |
| if (wakeLock) { | |
| await wakeLock.release().then(() =>{ | |
| console.info('WAKELOCK RELEASED'); | |
| wakeLock = null; | |
| }); | |
| } else { | |
| wakeLock = await navigator.wakeLock.request('screen'); | |
| console.info('WAKELOCK ON'); | |
| wakeLock.addEventListener('release', () => { | |
| // When released by system, update state and UI | |
| this.updateUI(); | |
| }); | |
| } | |
| } catch (err) { | |
| console.error(`Wake Lock request failed: ${err.name}, ${err.message}`); | |
| } finally { | |
| //console.log(`Wake Lock: ${wakeLock}`) | |
| //console.log(wakeLock) | |
| this.updateUI(); | |
| } | |
| } | |
| updateUI() { | |
| const button = this.shadowRoot.querySelector('#recipe-mode-btn'); | |
| if (wakeLock) { | |
| button.textContent = 'Recipe Mode ON'; | |
| button.classList.add('active'); | |
| } else { | |
| button.textContent = 'Recipe Mode OFF'; | |
| button.classList.remove('active'); | |
| } | |
| } | |
| } | |
| customElements.define('recipe-mode-toggle', RecipeModeToggle); | |
| </script> |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
FYI -- almost entirely written with gemini 2.5 pro because i was hella lazy. works though!
i had to do the insertion using a scroll trigger instead of a regular ready/domready event -- not sure why, kinda don't even care.