Claude Menu Click Blocker with Compact Hidden Controls

Blocks accidental clicks on Claude menu with space-saving hidden controls

// ==UserScript==
// @name         Claude Menu Click Blocker with Compact Hidden Controls
// @namespace    http://tampermonkey.net/
// @version      1.6
// @description  Blocks accidental clicks on Claude menu with space-saving hidden controls
// @author       Nirvash
// @match        https://claude.ai/chat/*
// @match        https://claude.ai/chats
// @grant        none
// @license MIT 
// ==/UserScript==

(function() {
    'use strict';

    let unlockTimer = null;
    const unlockDuration = 10000; // 10 seconds in milliseconds
    const blockerWidth = 250; // Width in pixels of the click-blocking area
    let isBlockerEnabled = true; // Default state is enabled
    let isTemporarilyUnblocked = false; // Track temporary unlock state
    let unlockEndTime = 0;

    function createClickBlocker() {
        console.log('Creating Claude menu click blocker...');

        // Remove existing blocker if any
        const existingBlocker = document.getElementById('claude-click-blocker');
        if (existingBlocker) {
            existingBlocker.remove();
        }

        // Only create if enabled and not temporarily unblocked
        if (!isBlockerEnabled || isTemporarilyUnblocked) {
            return;
        }

        // Create a transparent overlay div
        const blocker = document.createElement('div');
        blocker.id = 'claude-click-blocker';

        // Style the blocker
        blocker.style.position = 'fixed';
        blocker.style.top = '0';
        blocker.style.left = '0';
        blocker.style.width = `${blockerWidth}px`; // Width of the click-blocking area
        blocker.style.height = '100%';
        blocker.style.zIndex = '9999'; // Set z-index below our buttons
        blocker.style.pointerEvents = 'all'; // Capture all pointer events
        blocker.style.cursor = 'default'; // Default cursor to not indicate it's clickable

        // Add click handler
        blocker.addEventListener('click', function(e) {
            e.stopPropagation();
            e.preventDefault();
            console.log('Click blocked on Claude menu edge');
            return false;
        });

        // Add the blocker to the document
        document.body.appendChild(blocker);
        console.log('Claude menu click blocker added to page');
    }

    function createCompactControls() {
        // Remove existing controls if any
        const existingControls = document.getElementById('claude-blocker-controls');
        if (existingControls) {
            existingControls.remove();
        }

        // Create main container
        const container = document.createElement('div');
        container.id = 'claude-blocker-controls';

        // Style the container - initially collapsed
        container.style.position = 'fixed';
        container.style.top = '10px';
        container.style.left = '0';
        container.style.zIndex = '10001';
        container.style.display = 'flex';
        container.style.flexDirection = 'column';
        container.style.backgroundColor = 'rgba(255, 255, 255, 0.95)';
        container.style.borderRadius = '0 8px 8px 0';
        container.style.boxShadow = '2px 2px 10px rgba(0, 0, 0, 0.2)';
        container.style.transition = 'transform 0.3s ease';
        container.style.transform = 'translateX(-80%)'; // Initially mostly hidden
        container.style.overflow = 'hidden';

        // Status indicator (always visible part)
        const statusIndicator = document.createElement('div');
        statusIndicator.id = 'claude-blocker-status';
        statusIndicator.style.width = '30px';
        statusIndicator.style.height = '30px';
        statusIndicator.style.borderRadius = '0 4px 4px 0';
        statusIndicator.style.position = 'absolute';
        statusIndicator.style.right = '0';
        statusIndicator.style.top = '0';
        statusIndicator.style.display = 'flex';
        statusIndicator.style.alignItems = 'center';
        statusIndicator.style.justifyContent = 'center';
        statusIndicator.style.fontWeight = 'bold';
        statusIndicator.style.fontSize = '16px';
        updateStatusIndicator(statusIndicator);

        // Create buttons container
        const buttonsContainer = document.createElement('div');
        buttonsContainer.style.padding = '10px';
        buttonsContainer.style.display = 'flex';
        buttonsContainer.style.flexDirection = 'column';
        buttonsContainer.style.gap = '8px';
        buttonsContainer.style.minWidth = '120px'; // Ensure enough width for buttons

        // Create temporary unlock button
        const tempUnlockButton = document.createElement('button');
        tempUnlockButton.id = 'claude-temp-unlock-button';

        // Style the temporary unlock button - more compact
        tempUnlockButton.style.padding = '6px 10px';
        tempUnlockButton.style.color = 'white';
        tempUnlockButton.style.border = 'none';
        tempUnlockButton.style.borderRadius = '4px';
        tempUnlockButton.style.cursor = isBlockerEnabled ? 'pointer' : 'not-allowed';
        tempUnlockButton.style.fontSize = '12px';
        tempUnlockButton.style.fontWeight = 'bold';
        tempUnlockButton.style.width = '100%';
        tempUnlockButton.style.transition = 'background-color 0.2s';

        // Create toggle button
        const toggleButton = document.createElement('button');
        toggleButton.id = 'claude-toggle-button';

        // Style the toggle button - more compact
        toggleButton.style.padding = '6px 10px';
        toggleButton.style.color = 'white';
        toggleButton.style.border = 'none';
        toggleButton.style.borderRadius = '4px';
        toggleButton.style.cursor = 'pointer';
        toggleButton.style.fontSize = '12px';
        toggleButton.style.fontWeight = 'bold';
        toggleButton.style.width = '100%';
        toggleButton.style.transition = 'background-color 0.2s';

        // Set button states
        updateTempButtonState(tempUnlockButton);
        updateToggleButtonState(toggleButton);

        // Add button functionality
        tempUnlockButton.addEventListener('click', function() {
            toggleTemporaryUnlock();
            updateStatusIndicator(statusIndicator);
        });

        toggleButton.addEventListener('click', function() {
            toggleBlocker();
            updateStatusIndicator(statusIndicator);
        });

        // Add hover expand/collapse functionality
        container.addEventListener('mouseenter', function() {
            this.style.transform = 'translateX(0)'; // Fully expand
        });

        container.addEventListener('mouseleave', function() {
            this.style.transform = 'translateX(-80%)'; // Mostly hide
        });

        // Assemble the control elements
        container.appendChild(statusIndicator);
        buttonsContainer.appendChild(tempUnlockButton);
        buttonsContainer.appendChild(toggleButton);
        container.appendChild(buttonsContainer);

        // Add to document
        document.body.appendChild(container);
        console.log('Compact control panel created');
    }

    function updateStatusIndicator(indicator) {
        if (!indicator) {
            indicator = document.getElementById('claude-blocker-status');
            if (!indicator) return;
        }

        // Determine status color and icon
        let color, icon;

        if (!isBlockerEnabled) {
            color = '#2ecc71'; // Green for fully disabled
            icon = '🔓';
        } else if (isTemporarilyUnblocked) {
            color = '#f39c12'; // Orange for temporarily unblocked
            icon = '⏱️';
        } else {
            color = '#e74c3c'; // Red for active blocking
            icon = '🔒';
        }

        indicator.style.backgroundColor = color;
        indicator.textContent = icon;
    }

    function determineButtonColor(buttonType) {
        if (buttonType === 'temp') {
            // If blocker is disabled, temp button is disabled/gray
            if (!isBlockerEnabled) {
                return '#95a5a6'; // Gray for disabled state
            }
            // If temporarily unblocked, show orange
            return isTemporarilyUnblocked ? '#f39c12' : '#4a90e2';
        } else if (buttonType === 'toggle') {
            // Toggle button: red when enabled, green when disabled
            return isBlockerEnabled ? '#e74c3c' : '#2ecc71';
        }
        return '#4a90e2'; // Default blue
    }

    function updateTempButtonState(button) {
        if (!button) {
            button = document.getElementById('claude-temp-unlock-button');
            if (!button) return;
        }

        // Disable appearance if blocker is fully disabled
        if (!isBlockerEnabled) {
            button.textContent = '一時解除 (無効)';
            button.style.backgroundColor = '#95a5a6'; // Gray
            button.style.cursor = 'not-allowed';
            return;
        }

        // Normal state (enabled/unlocked)
        button.style.cursor = 'pointer';

        if (isTemporarilyUnblocked) {
            // Countdown state
            let secondsLeft = Math.ceil((unlockEndTime - Date.now()) / 1000);
            button.textContent = `⏱️ ${secondsLeft}秒後`;
            button.style.backgroundColor = '#f39c12'; // Orange
        } else {
            // Regular state
            button.textContent = '🔓 10秒間解除';
            button.style.backgroundColor = '#4a90e2'; // Blue
        }
    }

    function updateToggleButtonState(button) {
        if (!button) {
            button = document.getElementById('claude-toggle-button');
            if (!button) return;
        }

        button.textContent = isBlockerEnabled ? '🔒 ブロック中' : '🔓 解除中';
        button.style.backgroundColor = determineButtonColor('toggle');
    }

    function toggleBlocker() {
        isBlockerEnabled = !isBlockerEnabled;

        // Save state to localStorage
        localStorage.setItem('claudeBlockerEnabled', isBlockerEnabled.toString());

        // If disabling, cancel any temporary unlock
        if (!isBlockerEnabled) {
            isTemporarilyUnblocked = false;
            if (unlockTimer) {
                clearTimeout(unlockTimer);
                unlockTimer = null;
            }
        }

        // Update the UI
        updateToggleButtonState();
        updateTempButtonState();
        updateStatusIndicator();

        // Update the blocker
        if (isBlockerEnabled && !isTemporarilyUnblocked) {
            createClickBlocker();
        } else {
            const blocker = document.getElementById('claude-click-blocker');
            if (blocker) {
                blocker.remove();
            }
        }

        console.log(`Blocker ${isBlockerEnabled ? 'enabled' : 'disabled'}`);
    }

    function toggleTemporaryUnlock() {
        // If blocker is fully disabled, do nothing on temp button click
        if (!isBlockerEnabled) {
            return;
        }

        if (isTemporarilyUnblocked) {
            // Cancel temporary unlock early
            isTemporarilyUnblocked = false;
            if (unlockTimer) {
                clearTimeout(unlockTimer);
                unlockTimer = null;
            }
            createClickBlocker();
        } else {
            // Start temporary unlock
            isTemporarilyUnblocked = true;
            unlockEndTime = Date.now() + unlockDuration;

            // Remove the blocker
            const blocker = document.getElementById('claude-click-blocker');
            if (blocker) {
                blocker.remove();
            }

            // Clear any existing timer
            if (unlockTimer) {
                clearTimeout(unlockTimer);
            }

            // Start the countdown UI update
            startCountdown();

            // Set a timer to re-enable the blocker
            unlockTimer = setTimeout(() => {
                console.log('Temporary unlock period ended');
                unlockTimer = null;
                isTemporarilyUnblocked = false;
                if (isBlockerEnabled) { // Only recreate if globally enabled
                    createClickBlocker();
                }
                updateTempButtonState();
                updateStatusIndicator();
            }, unlockDuration);
        }

        // Update button states
        updateTempButtonState();
    }

    function startCountdown() {
        // Update every second
        const updateInterval = setInterval(() => {
            if (!isTemporarilyUnblocked || !isBlockerEnabled) {
                clearInterval(updateInterval);
                return;
            }

            const secondsLeft = Math.ceil((unlockEndTime - Date.now()) / 1000);

            if (secondsLeft <= 0) {
                clearInterval(updateInterval);
                return;
            }

            const tempButton = document.getElementById('claude-temp-unlock-button');
            if (tempButton) {
                tempButton.textContent = `⏱️ ${secondsLeft}秒後`;
            }

            updateStatusIndicator();
        }, 1000);
    }

    // Initialize the script
    function initialize() {
        console.log('Initializing Claude menu click blocker with compact controls');

        // Load saved state
        const savedState = localStorage.getItem('claudeBlockerEnabled');
        if (savedState !== null) {
            isBlockerEnabled = savedState === 'true';
        }

        // Reset temporary state on initialization
        isTemporarilyUnblocked = false;

        // Create blocker if enabled
        if (isBlockerEnabled) {
            createClickBlocker();
        }

        // Create control panel
        createCompactControls();
    }

    // Run on page load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        // DOM already loaded, run immediately
        initialize();
    }

    // Also run after a short delay to catch late-loading elements
    setTimeout(initialize, 1000);

    // Re-initialize on URL change (for SPA navigation)
    let lastUrl = location.href;
    new MutationObserver(() => {
        if (lastUrl !== location.href) {
            lastUrl = location.href;
            setTimeout(initialize, 500);
        }
    }).observe(document, {subtree: true, childList: true});

    // Add a periodic checker to ensure controls are visible and synced
    setInterval(() => {
        const controls = document.getElementById('claude-blocker-controls');
        if (!controls) {
            console.log('Controls missing, recreating...');
            createCompactControls();
        } else {
            // Update button states to ensure they're in sync
            updateTempButtonState();
            updateToggleButtonState();
            updateStatusIndicator();
        }
    }, 5000);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址