Blog Widescreen Responsive

Expands blog articles to use full widescreen space by removing restrictive max-width constraints on Substack, Medium, Ghost, WordPress and other platforms

// ==UserScript==
// @name         Blog Widescreen Responsive
// @namespace    https://greasyfork.runtimutd.eu.org/users/umsibaba
// @version      2.5.0
// @description  Expands blog articles to use full widescreen space by removing restrictive max-width constraints on Substack, Medium, Ghost, WordPress and other platforms
// @author       umsibaba
// @match        https://*.substack.com/*
// @match        https://substack.com/*
// @match        https://*.zerodha.com/*   
// @match        https://*.medium.com/*
// @match        https://medium.com/*
// @match        https://*.ghost.io/*
// @match        https://*.ghost.org/*
// @match        https://*.wordpress.com/*
// @match        https://*.wordpress.org/*
// @match        https://*.blogspot.com/*
// @match        https://*.blogger.com/*
// @match        https://*.dev.to/*
// @match        https://dev.to/*
// @match        https://*.hashnode.dev/*
// @match        https://*.notion.site/*
// @match        https://*.beehiiv.com/*
// @match        https://*.convertkit.com/*
// @match        https://*.mailchimp.com/*
// @match        https://*.github.io/*
// @match        https://*.netlify.app/*
// @match        https://*.vercel.app/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @supportURL   https://greasyfork.runtimutd.eu.org/scripts/yourscriptid/feedback
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    
    // Configuration
    const CONFIG = {
        maxWidth: GM_getValue('maxWidth', '95%'),
        padding: GM_getValue('padding', '2rem'),
        enabled: GM_getValue('enabled', true),
        preserveReadability: GM_getValue('preserveReadability', false),
        animationDuration: '0.3s'
    };
    
    // Platform-specific selectors using modern CSS techniques
    const PLATFORM_SELECTORS = {
        // Substack selectors
        substack: [
            '.single-post',
            '.post-content',
            '.reader2-post-content',
            '.post-header',
            '.frontend-pencraft-Box-module__Box--4o0Ow',
            'article[data-testid="post-content"]',
            '.post',
            'main article'
        ],
        
        // Medium selectors
        medium: [
            'article',
            '.postArticle-content',
            '.meteredContent',
            'main article',
            '[data-testid="storyBody"]',
            '.ab.cd.ce.cf.cg'
        ],
        
        // Ghost selectors
        ghost: [
            '.post-content',
            '.post-full-content',
            'article.post',
            '.gh-content',
            '.gh-article',
            'main article'
        ],
        
        // WordPress selectors
        wordpress: [
            '.post-content',
            '.entry-content',
            'article.post',
            '.single-post-container',
            'main article',
            '.wp-block-post-content'
        ],
        
        // Dev.to selectors
        devto: [
            '#article-wrapper',
            '.crayons-article',
            'article[data-article-path]',
            'main .crayons-article__main'
        ],
        
        // Hashnode selectors
        hashnode: [
            '.prose',
            'article',
            '.post-content-wrapper'
        ],
        
        // Generic blog selectors
        generic: [
            'article',
            '.post',
            '.post-content',
            '.entry-content',
            '.content',
            'main article',
            '[role="main"] article'
        ]
    };
    
    // Detect platform
    function detectPlatform() {
        const hostname = window.location.hostname.toLowerCase();
        
        if (hostname.includes('substack')) return 'substack';
        if (hostname.includes('medium')) return 'medium';
        if (hostname.includes('ghost')) return 'ghost';
        if (hostname.includes('wordpress') || hostname.includes('wp.')) return 'wordpress';
        if (hostname.includes('dev.to')) return 'devto';
        if (hostname.includes('hashnode')) return 'hashnode';
        
        return 'generic';
    }
    
    // Modern CSS styles using 2024/2025 features
    function createStyles() {
        return `
            /* Blog Widescreen Responsive - Fixed to expand content, not constrain it */
            
            /* CSS Custom Properties for dynamic theming */
            :root {
                --bwr-max-width: ${CONFIG.maxWidth};
                --bwr-padding: ${CONFIG.padding};
                --bwr-transition: all ${CONFIG.animationDuration} ease-in-out;
                --bwr-reading-measure: ${CONFIG.preserveReadability ? '75ch' : 'none'};
            }
            
            /* Remove restrictive max-width constraints from blog platforms */
            ${PLATFORM_SELECTORS[detectPlatform()].concat(PLATFORM_SELECTORS.generic)
                .map(selector => selector)
                .join(',\n            ')} {
                max-width: var(--bwr-max-width) !important;
                width: 100% !important;
                margin-left: auto !important;
                margin-right: auto !important;
                padding-left: var(--bwr-padding) !important;
                padding-right: var(--bwr-padding) !important;
                transition: var(--bwr-transition) !important;
                box-sizing: border-box !important;
            }
            
            /* Override platform-specific narrow constraints */
            .bwr-enhanced,
            .bwr-enhanced * {
                max-width: none !important;
            }
            
            /* Re-apply our desired max-width only to the main container */
            .bwr-enhanced {
                max-width: var(--bwr-max-width) !important;
                width: 100% !important;
                margin: 0 auto !important;
                padding-left: var(--bwr-padding) !important;
                padding-right: var(--bwr-padding) !important;
            }
            
            /* Typography enhancement - only if preserveReadability is enabled */
            ${CONFIG.preserveReadability ? `
            .bwr-enhanced :where(p, li, blockquote) {
                max-width: var(--bwr-reading-measure) !important;
                margin-left: auto !important;
                margin-right: auto !important;
            }` : ''}
            
            /* Responsive images and media - make them use full available width */
            .bwr-enhanced :where(img, video, iframe, embed, object, figure) {
                max-width: 100% !important;
                width: auto !important;
                height: auto !important;
                display: block !important;
                margin-left: auto !important;
                margin-right: auto !important;
            }
            
            /* Code blocks responsive handling */
            .bwr-enhanced :where(pre, code) {
                max-width: 100% !important;
                overflow-x: auto !important;
                white-space: pre-wrap !important;
                word-wrap: break-word !important;
            }
            
            /* Platform-specific fixes to remove narrow constraints */
            
            /* Substack specific overrides */
            .substack-post-content,
            .single-post,
            .post-content,
            .reader2-post-content,
            .frontend-pencraft-Box-module__Box--4o0Ow {
                max-width: var(--bwr-max-width) !important;
                width: 100% !important;
            }
            
            /* Medium specific overrides */
            .postArticle-content,
            .meteredContent,
            [data-testid="storyBody"] {
                max-width: var(--bwr-max-width) !important;
                width: 100% !important;
            }
            
            /* Ghost specific overrides */
            .post-content,
            .post-full-content,
            .gh-content,
            .gh-article {
                max-width: var(--bwr-max-width) !important;
                width: 100% !important;
            }
            
            /* WordPress specific overrides */
            .entry-content,
            .post-content,
            .wp-block-post-content {
                max-width: var(--bwr-max-width) !important;
                width: 100% !important;
            }
            
            /* Responsive breakpoints */
            @media (max-width: 768px) {
                .bwr-enhanced {
                    padding-left: 1rem !important;
                    padding-right: 1rem !important;
                }
            }
            
            @media (min-width: 1600px) {
                .bwr-enhanced {
                    max-width: 95% !important;
                }
            }
            
            @media (min-width: 1920px) {
                .bwr-enhanced {
                    max-width: 90% !important;
                }
            }
            
            /* Dark mode support */
            @media (prefers-color-scheme: dark) {
                .bwr-enhanced {
                    color-scheme: dark;
                }
            }
            
            /* Reduced motion support */
            @media (prefers-reduced-motion: reduce) {
                .bwr-enhanced {
                    transition: none !important;
                }
            }
            
            /* Status indicator */
            .bwr-status {
                position: fixed;
                top: 10px;
                right: 10px;
                background: #4CAF50;
                color: white;
                padding: 8px 12px;
                border-radius: 6px;
                font-size: 12px;
                font-family: system-ui, -apple-system, sans-serif;
                z-index: 10000;
                opacity: 0;
                transform: translateX(100%);
                transition: all 0.3s ease;
                cursor: pointer;
                user-select: none;
            }
            
            .bwr-status.show {
                opacity: 1;
                transform: translateX(0);
            }
            
            .bwr-status.disabled {
                background: #f44336;
            }
        `;
    }
    
    // Enhanced element detection using modern selectors
    function findContentElements() {
        const platform = detectPlatform();
        const selectors = PLATFORM_SELECTORS[platform].concat(PLATFORM_SELECTORS.generic);
        
        for (const selector of selectors) {
            const elements = document.querySelectorAll(selector);
            if (elements.length > 0) {
                return Array.from(elements).filter(el => {
                    // Use getBoundingClientRect for better detection
                    const rect = el.getBoundingClientRect();
                    return rect.width > 100 && rect.height > 100;
                });
            }
        }
        
        return [];
    }
    
    // Apply enhancements to elements
    function applyEnhancements() {
        if (!CONFIG.enabled) return;
        
        const elements = findContentElements();
        
        elements.forEach(element => {
            if (!element.classList.contains('bwr-enhanced')) {
                element.classList.add('bwr-enhanced');
                
                // Add container query support for modern browsers
                if (CSS.supports('container-type: inline-size')) {
                    element.style.containerType = 'inline-size';
                }
            }
        });
        
        showStatus(`Enhanced ${elements.length} elements`);
    }
    
    // Status notification
    function showStatus(message) {
        let status = document.querySelector('.bwr-status');
        if (!status) {
            status = document.createElement('div');
            status.className = 'bwr-status';
            status.addEventListener('click', toggleScript);
            document.body.appendChild(status);
        }
        
        status.textContent = CONFIG.enabled ? `✓ BWR: ${message}` : '✗ BWR: Disabled';
        status.classList.toggle('disabled', !CONFIG.enabled);
        status.classList.add('show');
        
        setTimeout(() => {
            status.classList.remove('show');
        }, 3000);
    }
    
    // Toggle script functionality
    function toggleScript() {
        CONFIG.enabled = !CONFIG.enabled;
        GM_setValue('enabled', CONFIG.enabled);
        
        if (CONFIG.enabled) {
            applyEnhancements();
        } else {
            document.querySelectorAll('.bwr-enhanced').forEach(el => {
                el.classList.remove('bwr-enhanced');
            });
        }
        
        showStatus(CONFIG.enabled ? 'Enabled' : 'Disabled');
    }
    
    // Settings management
    function openSettings() {
        const newMaxWidth = prompt('Enter max width (e.g., 95%, 1400px, or 100%):', CONFIG.maxWidth);
        if (newMaxWidth) {
            CONFIG.maxWidth = newMaxWidth;
            GM_setValue('maxWidth', newMaxWidth);
        }
        
        const newPadding = prompt('Enter side padding (e.g., 2rem, 20px):', CONFIG.padding);
        if (newPadding) {
            CONFIG.padding = newPadding;
            GM_setValue('padding', newPadding);
        }
        
        const preserveReadability = confirm('Limit paragraph width for better readability? (Unchecked = full width)');
        CONFIG.preserveReadability = preserveReadability;
        GM_setValue('preserveReadability', preserveReadability);
        
        // Reapply styles with new settings
        initializeScript();
        showStatus('Settings updated');
    }
    
    // Main initialization
    function initializeScript() {
        // Inject CSS
        GM_addStyle(createStyles());
        
        // Apply enhancements immediately
        applyEnhancements();
        
        // Use modern ResizeObserver for better performance
        if (window.ResizeObserver) {
            const resizeObserver = new ResizeObserver(() => {
                applyEnhancements();
            });
            resizeObserver.observe(document.body);
        }
        
        // Fallback to MutationObserver for content changes
        const mutationObserver = new MutationObserver((mutations) => {
            let shouldReapply = false;
            mutations.forEach(mutation => {
                if (mutation.addedNodes.length > 0) {
                    shouldReapply = true;
                }
            });
            
            if (shouldReapply) {
                setTimeout(applyEnhancements, 100);
            }
        });
        
        mutationObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
    }
    
    // Register menu commands
    GM_registerMenuCommand('Toggle Blog Widescreen', toggleScript);
    GM_registerMenuCommand('BWR Settings', openSettings);
    
    // Wait for DOM and initialize
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initializeScript);
    } else {
        initializeScript();
    }
    
    // Handle SPA navigation
    let lastUrl = location.href;
    new MutationObserver(() => {
        const url = location.href;
        if (url !== lastUrl) {
            lastUrl = url;
            setTimeout(applyEnhancements, 500);
        }
    }).observe(document, { subtree: true, childList: true });
    
})();