您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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);
- })();