Skip to content

Instantly share code, notes, and snippets.

@nealrs
Last active October 9, 2025 15:00
Show Gist options
  • Select an option

  • Save nealrs/f0538c02c90cf90f3e993478572a650c to your computer and use it in GitHub Desktop.

Select an option

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
<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>
@nealrs
Copy link
Author

nealrs commented Oct 8, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment