YouTube Rich Grid Columns Changer (Override Inline Style)

Override YouTube's inline style for rich grid columns using MutationObserver.

// ==UserScript==
// @name         YouTube Rich Grid Columns Changer (Override Inline Style)
// @namespace    http://tampermonkey.net/
// @version      0.6
// @description  Override YouTube's inline style for rich grid columns using MutationObserver.
// @author       fargly
// @match        https://www.youtube.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // --- Safeguard: Ensure script runs only in the top-level window ---
    // This check is crucial to prevent the script from running in sandboxed iframes.
    if (window.top !== window) {
        return; // Stop script execution immediately if in an iframe
    }

    console.log('Tampermonkey script: Running in the top-level window. Preparing to override YouTube inline styles...');

    // --- Configuration ---
    // Set your desired number of posts per row here.
    const newValue = 5; // <--- Set your desired number of posts per row

    // --- Script Logic ---
    // Function to apply the desired style override
    function applyGridColumnsOverride() {
        // Defensive check, though the initial safeguard should prevent execution in iframes
        if (window.top !== window) {
            return;
        }

        // Target the element that typically holds YouTube's inline grid styles.
        // ytd-rich-grid-renderer is the most probable element for these variables.
        const gridElement = document.querySelector('ytd-rich-grid-renderer');

        if (gridElement) {
            // Apply the style directly to the element where YouTube sets its inline styles.
            // Setting the property on the element's style object will override
            // any value set by YouTube's script for this specific property.
            gridElement.style.setProperty('--ytd-rich-grid-posts-per-row', newValue);
            // This element needed to be added to change the visual
            gridElement.style.setProperty('--ytd-rich-grid-items-per-row', newValue);
            console.log(`Tampermonkey script: Attempted to set --ytd-rich-grid-posts-per-row on ytd-rich-grid-renderer to ${newValue}`);

            // You can optionally inspect the element in the browser's developer tools
            // after the script runs to see if the style is applied. It should appear
            // within the element's style="..." attribute.

        } else {
            console.log('Tampermonkey script: Could not find the grid element (ytd-rich-grid-renderer).');
            // This might happen if the element hasn't loaded yet, or YouTube's structure changed.
        }
    }

    // --- MutationObserver to re-apply style if YouTube overrides it ---
    let observer = null; // Variable to hold the MutationObserver instance

    function setupObserver() {
        // Disconnect any existing observer before setting up a new one
        if (observer) {
            observer.disconnect();
            observer = null; // Clear the reference
        }

        const gridElement = document.querySelector('ytd-rich-grid-renderer');

        if (gridElement) {
            // Create a new MutationObserver
            observer = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    // We are specifically watching for changes to the 'style' attribute
                    if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
                        console.log('Tampermonkey script: Style attribute changed on ytd-rich-grid-renderer. Re-applying override.');
                        // Re-apply our desired style whenever the style attribute changes
                        applyGridColumnsOverride();
                    }
                });
            });

            // Start observing the target element for attribute changes
            observer.observe(gridElement, {
                attributes: true // Configure observer to watch for attribute changes
            });

             console.log('Tampermonkey script: MutationObserver set up on ytd-rich-grid-renderer.');

        } else {
            console.log('Tampermonkey script: Could not find ytd-rich-grid-renderer to set up observer.');
            // If the element isn't found immediately, the observer can't be attached.
            // We rely on the initial timeout and the yt-navigate-finish listener to find it later.
        }
    }

    // --- Execution Triggers ---

    // 1. Initial attempt after the DOM is loaded and a short delay
    window.addEventListener('DOMContentLoaded', () => {
        // Use a timeout to give YouTube's elements time to render after DOM is ready
        setTimeout(() => {
            applyGridColumnsOverride(); // Apply style initially
            setupObserver(); // Set up the observer after the element should be present
        }, 2000); // 2 second delay - adjust if needed
    });

    // 2. Re-apply style and reset observer on YouTube's internal navigation
    // This is necessary because YouTube is a Single Page Application (SPA),
    // and the ytd-rich-grid-renderer element might be replaced or updated on navigation.
    window.addEventListener('yt-navigate-finish', () => {
        console.log('Tampermonkey script: Navigation finished. Re-applying style and attempting to re-set up observer.');
        // Use a short delay after navigation to allow new elements to load
        setTimeout(() => {
             applyGridColumnsOverride(); // Re-apply style on navigation
             setupObserver(); // Re-set up observer on the potentially new element
        }, 500); // 0.5 second delay after navigation
    });


    // --- Cleanup ---
    // Disconnect the observer when the page is unloaded to prevent memory leaks.
     window.addEventListener('beforeunload', () => {
         console.log('Tampermonkey script: Cleaning up observer.');
         if (observer) {
             observer.disconnect();
         }
         // Note: Removing event listeners added with anonymous functions or inside other functions
         // in SPAs can be complex. Disconnecting the observer is the most critical cleanup here.
     });


})();