图片隐藏

单张或者全局图片隐藏与显示

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         图片隐藏
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  单张或者全局图片隐藏与显示
// @match        *://*/*
// @author       eternal5130
// @license      MIT
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    const STORAGE_KEY_SHORTCUT = 'imageHiderShortcut_v2';
    const STORAGE_KEY_GLOBAL_HIDE = 'imageHiderGlobalHideState_v2';
    const CSS_CLASS_HIDDEN = 'image-hider--hidden';
    const CSS_CLASS_OVERLAY = 'image-hider--overlay';
    const CSS_MODAL_ID = 'image-hider-modal-styles';
    const DEFAULT_SHORTCUT = {
        single: { keys: ['MouseMiddle'] },
        global: { keys: ['AltLeft', 'KeyH'] }
    };
    const DEBOUNCE_DELAY = 250;
    const THROTTLE_DELAY = 50;

    const imageStates = new Map();
    const currentPressedKeys = new Set();
    let currentHoverTarget = null;
    let configModal = null;
    let globalImageCounter = 0;
    let mutationObserver = null;

    const KEY_NAME_MAP = {
        'AltLeft': 'Alt (左)', 'AltRight': 'Alt (右)',
        'ControlLeft': 'Ctrl (左)', 'ControlRight': 'Ctrl (右)',
        'ShiftLeft': 'Shift (左)', 'ShiftRight': 'Shift (右)',
        'MetaLeft': 'Meta (左)', 'MetaRight': 'Meta (右)',
        'MouseLeft': '鼠标左键', 'MouseMiddle': '鼠标中键', 'MouseRight': '鼠标右键',
        'KeyA': 'A', 'KeyB': 'B', 'KeyC': 'C', 'KeyD': 'D', 'KeyE': 'E',
        'KeyF': 'F', 'KeyG': 'G', 'KeyH': 'H', 'KeyI': 'I', 'KeyJ': 'J',
        'KeyK': 'K', 'KeyL': 'L', 'KeyM': 'M', 'KeyN': 'N', 'KeyO': 'O',
        'KeyP': 'P', 'KeyQ': 'Q', 'KeyR': 'R', 'KeyS': 'S', 'KeyT': 'T',
        'KeyU': 'U', 'KeyV': 'V', 'KeyW': 'W', 'KeyX': 'X', 'KeyY': 'Y', 'KeyZ': 'Z',
        'Digit0': '0', 'Digit1': '1', 'Digit2': '2', 'Digit3': '3',
        'Digit4': '4', 'Digit5': '5', 'Digit6': '6', 'Digit7': '7',
        'Digit8': '8', 'Digit9': '9'
    };

    function getKeyDisplayName(code) {
        return KEY_NAME_MAP[code] || code;
    }

    function throttle(func, limit) {
        let inThrottle;
        return function(...args) {
            const context = this;
            if (!inThrottle) {
                func.apply(context, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        }
    }

    function getShortcutConfig() {
        try {
            const savedConfig = GM_getValue(STORAGE_KEY_SHORTCUT);
            const parsed = savedConfig ? JSON.parse(savedConfig) : {};
            return {
                single: { keys: parsed?.single?.keys || DEFAULT_SHORTCUT.single.keys },
                global: { keys: parsed?.global?.keys || DEFAULT_SHORTCUT.global.keys }
            };
        } catch (error) {
            // console.error('Image Hider: Failed to get shortcut config, using defaults.', error);
            return { ...DEFAULT_SHORTCUT };
        }
    }

    function saveShortcutConfig(config) {
        try {
            GM_setValue(STORAGE_KEY_SHORTCUT, JSON.stringify(config));
        } catch (error) {
            // console.error('Image Hider: Failed to save shortcut config.', error);
            alert('保存配置时出错,请重试');
        }
    }

    function getGlobalHideState() {
        return GM_getValue(STORAGE_KEY_GLOBAL_HIDE, false);
    }

    function setGlobalHideState(state) {
        GM_setValue(STORAGE_KEY_GLOBAL_HIDE, state);
    }

    class ImageState {
        constructor(imgElement) {
            this.element = imgElement;
            this.isHidden = false;
            this.overlay = null;
            this.observers = new Set();
            this.lastToggleTime = 0;
            this.userManuallyShown = false;
            this.isProcessing = false;
        }

        cleanup() {
            this.observers.forEach(observer => observer.disconnect());
            this.observers.clear();
            if (this.overlay) {
                this.overlay.cleanup?.();
                this.overlay.remove();
                this.overlay = null;
            }
        }
    }

    function ensureImageState(imgElement) {
        if (!imgElement || !(imgElement instanceof HTMLImageElement)) return null;
        let imageId = imgElement.dataset.imageId;
        if (!imageId) {
            imageId = `img_hider_${++globalImageCounter}`;
            imgElement.dataset.imageId = imageId;
        }
        if (!imageStates.has(imageId)) {
            imageStates.set(imageId, new ImageState(imgElement));
        }
        return imageStates.get(imageId);
    }

    function createOverlay(imgElement, state) {
        if (!imgElement?.parentNode || !state) return null;

        const overlay = document.createElement('div');
        overlay.className = CSS_CLASS_OVERLAY;
        overlay.dataset.linkedImageId = imgElement.dataset.imageId;

        const updatePosition = throttle(() => {
            if (!imgElement || !imgElement.parentNode || !document.body.contains(imgElement)) {
                state.cleanup();
                imageStates.delete(imgElement.dataset.imageId);
                return;
            }
            const rect = imgElement.getBoundingClientRect();
            if (rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth) {
                 Object.assign(overlay.style, {
                    left: `${rect.left + window.scrollX}px`,
                    top: `${rect.top + window.scrollY}px`,
                    width: `${rect.width}px`,
                    height: `${rect.height}px`,
                });
                 if (!document.body.contains(overlay)) {
                    document.body.appendChild(overlay);
                 }
            }
        }, THROTTLE_DELAY);

        requestAnimationFrame(updatePosition);

        const intersectionObserver = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    requestAnimationFrame(updatePosition);
                }
            });
        }, { threshold: 0.1 });
        intersectionObserver.observe(imgElement);
        state.observers.add(intersectionObserver);

        const resizeObserver = new ResizeObserver(() => requestAnimationFrame(updatePosition));
        resizeObserver.observe(imgElement);
        state.observers.add(resizeObserver);

        const scrollHandler = () => requestAnimationFrame(updatePosition);
        window.addEventListener('scroll', scrollHandler, { passive: true, capture: true });

        overlay.addEventListener('mouseover', () => {
            currentHoverTarget = state.element;
        });
        overlay.addEventListener('mouseout', () => {
            currentHoverTarget = null;
        });
        overlay.addEventListener('mousedown', (e) => {
            if (e.button === 1) {
                e.preventDefault();
                 const shortcutConfig = getShortcutConfig();
                 if (shortcutConfig.single.keys.includes('MouseMiddle') && currentHoverTarget === state.element) {
                     currentPressedKeys.add('MouseMiddle');
                     checkShortcut();
                     currentPressedKeys.delete('MouseMiddle');
                 } else {
                    toggleImageVisibility(state.element);
                 }
            }
        });

        overlay.cleanup = () => {
            window.removeEventListener('scroll', scrollHandler, { capture: true });
        };

        document.body.appendChild(overlay);
        return overlay;
    }

    function toggleImageVisibility(imgElement, forceHide = null, isInitialAutoHide = false) {
        const state = ensureImageState(imgElement);
        if (!state || !state.element?.parentNode || !document.body.contains(state.element)) {
             if (state && imgElement?.dataset?.imageId) {
                 state.cleanup();
                 imageStates.delete(imgElement.dataset.imageId);
             }
             return;
        }

        const currentTime = Date.now();
        if (state.isProcessing || (currentTime - state.lastToggleTime < DEBOUNCE_DELAY)) {
            return;
        }

        state.isProcessing = true;
        state.lastToggleTime = currentTime;

        if (isInitialAutoHide && state.userManuallyShown) {
            state.isProcessing = false;
            return;
        }

        const shouldHide = (forceHide !== null) ? forceHide : !state.isHidden;

        if (!isInitialAutoHide && !shouldHide) {
            state.userManuallyShown = true;
        } else if (!isInitialAutoHide && shouldHide) {
             state.userManuallyShown = false;
        }

        try {
            if (shouldHide) {
                state.element.classList.add(CSS_CLASS_HIDDEN);
                if (!state.overlay) {
                    state.overlay = createOverlay(state.element, state);
                }
                 if (state.overlay && !document.body.contains(state.overlay)) {
                    document.body.appendChild(state.overlay);
                 }

            } else {
                state.element.classList.remove(CSS_CLASS_HIDDEN);
                if (state.overlay) {
                   state.cleanup();
                }
            }
            state.isHidden = shouldHide;

        } catch (error) {
            // console.error('Image Hider: Error toggling visibility for', state.element, error);
        } finally {
           setTimeout(() => {
              state.isProcessing = false;
           }, DEBOUNCE_DELAY + 50);
        }
    }

    function toggleAllImages() {
        const images = document.getElementsByTagName('img');
        const shouldHide = !getGlobalHideState();

        setGlobalHideState(shouldHide);

        requestAnimationFrame(() => {
            Array.from(images).forEach(img => {
                ensureImageState(img);
                toggleImageVisibility(img, shouldHide, false);
            });
             if (shouldHide) {
                imageStates.forEach(state => state.userManuallyShown = false);
             }
        });
    }

    function processImages(imageList, applyGlobalState) {
         Array.from(imageList).forEach(img => {
            const state = ensureImageState(img);
            if (state && applyGlobalState) {
                 if (!state.isHidden) {
                    img.classList.add(CSS_CLASS_HIDDEN);
                    toggleImageVisibility(img, true, true);
                 }
            } else if (state && !applyGlobalState && state.isHidden) {
                 toggleImageVisibility(img, false, false);
            }
         });
    }

    function initAutoHideAndObserver() {
        const globalShouldHide = getGlobalHideState();

        processImages(document.getElementsByTagName('img'), globalShouldHide);

        if (mutationObserver) mutationObserver.disconnect();

        mutationObserver = new MutationObserver((mutations) => {
            const currentGlobalHideState = getGlobalHideState();

            mutations.forEach(mutation => {
                if (mutation.addedNodes.length) {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const newImages = [];
                            if (node.tagName === 'IMG') {
                                newImages.push(node);
                            } else if (node.querySelectorAll) {
                                newImages.push(...node.querySelectorAll('img'));
                            }
                            if (newImages.length > 0) {
                                processImages(newImages, currentGlobalHideState);
                            }
                        }
                    });
                }

                if (mutation.removedNodes.length) {
                    mutation.removedNodes.forEach(node => {
                         if (node.nodeType === Node.ELEMENT_NODE) {
                             const removedImages = [];
                             if (node.tagName === 'IMG' && node.dataset.imageId) {
                                 removedImages.push(node);
                             } else if (node.querySelectorAll) {
                                 removedImages.push(...node.querySelectorAll('img[data-image-id]'));
                             }

                             removedImages.forEach(img => {
                                 const imageId = img.dataset.imageId;
                                 if (imageId && imageStates.has(imageId)) {
                                     const state = imageStates.get(imageId);
                                     state.cleanup();
                                     imageStates.delete(imageId);
                                 }
                             });
                         }
                    });
                }

                 if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
                     const imgElement = mutation.target;
                     if (imgElement.tagName === 'IMG' && getGlobalHideState()) {
                         const state = ensureImageState(imgElement);
                         if (state && !state.isHidden) {
                             imgElement.classList.add(CSS_CLASS_HIDDEN);
                             toggleImageVisibility(imgElement, true, true);
                         }
                     }
                 }

            });
        });

        mutationObserver.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['src']
        });
    }


    function handleKeyDown(event) {
        const targetTagName = event.target.tagName.toLowerCase();
        if (['input', 'textarea', 'select'].includes(targetTagName) || event.target.isContentEditable) {
            return;
        }
        currentPressedKeys.add(event.code);
        checkShortcut();
    }

    function handleKeyUp(event) {
        currentPressedKeys.delete(event.code);
    }

    function handleMouseDown(event) {
        if (event.button === 1) {
             const shortcutConfig = getShortcutConfig();
             if (shortcutConfig.single.keys.includes('MouseMiddle') && currentHoverTarget) {
                event.preventDefault();
             }
            currentPressedKeys.add('MouseMiddle');
            checkShortcut();
        }
    }

    function handleMouseUp(event) {
        if (event.button === 1) {
            currentPressedKeys.delete('MouseMiddle');
        }
    }

    function handleMouseOver(event) {
        if (event.target.tagName === 'IMG') {
            currentHoverTarget = event.target;
            ensureImageState(currentHoverTarget);
        } else if (event.target.classList?.contains(CSS_CLASS_OVERLAY)) {
             const linkedImageId = event.target.dataset.linkedImageId;
             const state = linkedImageId ? imageStates.get(linkedImageId) : null;
             if (state?.element) {
                 currentHoverTarget = state.element;
             }
        }
    }

    function handleMouseOut(event) {
         if (event.target === currentHoverTarget || event.target.classList?.contains(CSS_CLASS_OVERLAY)) {
             if (!event.relatedTarget || (event.relatedTarget !== currentHoverTarget && !event.relatedTarget.classList?.contains(CSS_CLASS_OVERLAY))) {
                currentHoverTarget = null;
             }
         }
    }

    function resetStateOnUnload() {
        currentPressedKeys.clear();
        currentHoverTarget = null;
        if (mutationObserver) {
            mutationObserver.disconnect();
            mutationObserver = null;
        }
    }

    function checkShortcut() {
        const shortcutConfig = getShortcutConfig();

        const isExactMatch = (keys) => {
            return keys.length === currentPressedKeys.size && keys.every(key => currentPressedKeys.has(key));
        }

        if (currentHoverTarget && isExactMatch(shortcutConfig.single.keys)) {
            toggleImageVisibility(currentHoverTarget);
        }

        if (isExactMatch(shortcutConfig.global.keys)) {
            toggleAllImages();
            currentPressedKeys.clear();
        }
    }


    function createConfigModal() {
         if (configModal) return;

         injectGlobalStyles();

         configModal = document.createElement('div');
         configModal.className = 'image-hider-modal';
         configModal.id = 'image-hider-config-modal';

         const currentConfig = getShortcutConfig();
         let tempSingleKeys = new Set(currentConfig.single.keys);
         let tempGlobalKeys = new Set(currentConfig.global.keys);
         let isRecording = false;
         let currentRecordingType = null;
         let recordingCleanup = null;

         function formatKeys(keys) {
             return Array.from(keys).map(getKeyDisplayName).join(' + ') || '无';
         }

         function renderModalContent() {
             const singleShortcutDisplay = formatKeys(tempSingleKeys);
             const globalShortcutDisplay = formatKeys(tempGlobalKeys);

             configModal.innerHTML = `
                <button class="close-btn" id="imageHiderCloseConfigBtn" title="关闭"></button>
                <h2>图片隐藏快捷键配置</h2>
                <div class="shortcut-section">
                    <h3>单个图片快捷键 (悬停触发)</h3>
                    <div class="current-shortcut"><strong>当前:</strong> <span id="singleCurrentDisplay">${singleShortcutDisplay}</span></div>
                    <input type="text" readonly class="shortcut-input" id="singleShortcutInput" placeholder="点击下方按钮开始录制...">
                    <div class="button-wrapper"><button id="setSingleShortcutBtn" data-type="single">设置快捷键</button></div>
                </div>
                <div class="shortcut-section">
                    <h3>全局图片快捷键 (切换所有)</h3>
                    <div class="current-shortcut"><strong>当前:</strong> <span id="globalCurrentDisplay">${globalShortcutDisplay}</span></div>
                    <input type="text" readonly class="shortcut-input" id="globalShortcutInput" placeholder="点击下方按钮开始录制...">
                    <div class="button-wrapper"><button id="setGlobalShortcutBtn" data-type="global">设置快捷键</button></div>
                </div>
                <div class="button-group"><button id="saveConfigBtn">保存设置</button></div>
                <div class="hint">说明: 录制时按下组合键,松开最后一个键完成录制。支持 Alt, Ctrl, Shift, Meta (Win/Cmd), 字母, 数字, 鼠标中键。全局状态会在页面加载时自动应用。</div>
                <div id="imageHiderSuccessMessage" class="success-message"></div>
            `;
             attachModalListeners();
         }

         function updateInputDisplay(inputElement, keysSet) {
             inputElement.value = formatKeys(keysSet);
         }

         function startRecording(type) {
             if (isRecording) return;
             const inputElement = configModal.querySelector(`#${type}ShortcutInput`);
             const buttonElement = configModal.querySelector(`#set${type.charAt(0).toUpperCase() + type.slice(1)}ShortcutBtn`);
             const keysSet = (type === 'single') ? tempSingleKeys : tempGlobalKeys;

             isRecording = true;
             currentRecordingType = type;
             keysSet.clear();

             inputElement.value = '请按下快捷键...';
             inputElement.classList.add('recording');
             buttonElement.textContent = '录制中... (松开完成)';
             buttonElement.disabled = true;

             const pressedWhileRecording = new Set();

             const keydownHandler = (e) => {
                 e.preventDefault(); e.stopPropagation();
                 const code = e.code;
                 if (!pressedWhileRecording.has(code)) {
                     pressedWhileRecording.add(code);
                     keysSet.add(code);
                     updateInputDisplay(inputElement, keysSet);
                 }
             };
             const mousedownHandler = (e) => {
                  if (e.button === 1) {
                     e.preventDefault(); e.stopPropagation();
                     const code = 'MouseMiddle';
                      if (!pressedWhileRecording.has(code)) {
                         pressedWhileRecording.add(code);
                         keysSet.add(code);
                         updateInputDisplay(inputElement, keysSet);
                     }
                  }
             };
             const stopRecordingHandler = (e) => {
                 if (pressedWhileRecording.size > 0) {
                     e.preventDefault(); e.stopPropagation();
                     finishRecording();
                 }
             };

             document.addEventListener('keydown', keydownHandler, { capture: true });
             document.addEventListener('mousedown', mousedownHandler, { capture: true });
             document.addEventListener('keyup', stopRecordingHandler, { capture: true, once: true });
             document.addEventListener('mouseup', stopRecordingHandler, { capture: true, once: true });

             recordingCleanup = () => {
                 document.removeEventListener('keydown', keydownHandler, { capture: true });
                 document.removeEventListener('mousedown', mousedownHandler, { capture: true });
                 document.removeEventListener('keyup', stopRecordingHandler, { capture: true });
                 document.removeEventListener('mouseup', stopRecordingHandler, { capture: true });
                 recordingCleanup = null;
             };
         }

         function finishRecording() {
             if (!isRecording) return;
             const type = currentRecordingType;
             const inputElement = configModal.querySelector(`#${type}ShortcutInput`);
             const buttonElement = configModal.querySelector(`#set${type.charAt(0).toUpperCase() + type.slice(1)}ShortcutBtn`);
             const keysSet = (type === 'single') ? tempSingleKeys : tempGlobalKeys;

             isRecording = false;
             currentRecordingType = null;

             inputElement.classList.remove('recording');
             buttonElement.textContent = '设置快捷键';
             buttonElement.disabled = false;
              updateInputDisplay(inputElement, keysSet);
              if (keysSet.size === 0) {
                 inputElement.value = '';
                 inputElement.placeholder = '未设置 - 点击按钮录制';
              }

             if (recordingCleanup) recordingCleanup();
         }

         function closeModal() {
             if (isRecording) finishRecording();
             if (configModal) {
                configModal.classList.add('closing');
                configModal.addEventListener('transitionend', () => {
                     configModal?.remove();
                     configModal = null;
                 }, { once: true });
             }
              document.removeEventListener('mousedown', backdropClickHandler);
         }

         function showSuccessMessage(message) {
             const msgElement = configModal?.querySelector('#imageHiderSuccessMessage');
             if (msgElement) {
                 msgElement.textContent = message;
                 msgElement.classList.add('show');
                 setTimeout(() => {
                     msgElement.classList.remove('show');
                 }, 1500);
             }
         }

         function backdropClickHandler(event) {
             if (configModal && !configModal.contains(event.target)) {
                 closeModal();
             }
         }

         function attachModalListeners() {
             configModal.querySelector('#imageHiderCloseConfigBtn').addEventListener('click', closeModal);
             configModal.querySelector('#setSingleShortcutBtn').addEventListener('click', () => startRecording('single'));
             configModal.querySelector('#setGlobalShortcutBtn').addEventListener('click', () => startRecording('global'));
             configModal.querySelector('#saveConfigBtn').addEventListener('click', () => {
                 if (isRecording) finishRecording();
                 const newConfig = {
                     single: { keys: Array.from(tempSingleKeys) },
                     global: { keys: Array.from(tempGlobalKeys) }
                 };
                 saveShortcutConfig(newConfig);
                 configModal.querySelector('#singleCurrentDisplay').textContent = formatKeys(tempSingleKeys);
                 configModal.querySelector('#globalCurrentDisplay').textContent = formatKeys(tempGlobalKeys);
                 showSuccessMessage('设置已保存!');
             });
             setTimeout(() => { document.addEventListener('mousedown', backdropClickHandler); }, 0);
         }

         renderModalContent();
         document.body.appendChild(configModal);
         requestAnimationFrame(() => { configModal.style.opacity = '1'; });
     }

    function injectGlobalStyles() {
        if (document.getElementById(CSS_MODAL_ID)) return;
        GM_addStyle(`
            .${CSS_CLASS_HIDDEN} { opacity: 0 !important; pointer-events: none !important; transition: opacity 0.2s ease !important; }
            .${CSS_CLASS_OVERLAY} { position: absolute; background-color: transparent; border: 1px dashed rgba(128, 128, 128, 0.3); cursor: pointer; pointer-events: auto; z-index: 9998; box-sizing: border-box; }
            .image-hider-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #fff; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); padding: 28px; z-index: 10000; width: 90%; max-width: 480px; max-height: 85vh; overflow-y: auto; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; opacity: 0; transition: opacity 0.25s ease-out, transform 0.25s ease-out; box-sizing: border-box; }
            .image-hider-modal.closing { opacity: 0; transform: translate(-50%, -45%); transition: opacity 0.2s ease-in, transform 0.2s ease-in; }
            .image-hider-modal * { box-sizing: border-box; }
            .image-hider-modal h2 { color: #333; margin: 0 0 24px 0; font-size: 1.4em; font-weight: 600; text-align: center; }
            .image-hider-modal .shortcut-section { background: #f7f7f7; border-radius: 8px; padding: 16px; margin-bottom: 20px; border: 1px solid #eee; }
            .image-hider-modal .shortcut-section h3 { color: #444; margin: 0 0 12px 0; font-size: 1.1em; font-weight: 500; }
            .image-hider-modal .current-shortcut { background-color: #fff; padding: 10px 14px; margin-bottom: 12px; border-radius: 6px; border: 1px solid #e0e0e0; color: #555; font-size: 0.95em; min-height: 40px; display: flex; align-items: center; }
            .image-hider-modal .current-shortcut strong { margin-right: 8px; }
            .image-hider-modal .shortcut-input { width: 100%; height: 40px; padding: 8px 12px; margin: 8px 0 12px 0; border: 1px solid #ccc; border-radius: 6px; font-size: 1em; text-align: center; background: #fff; color: #333; transition: border-color 0.2s, box-shadow 0.2s; }
            .image-hider-modal .shortcut-input:focus, .image-hider-modal .shortcut-input.recording { border-color: #4CAF50; outline: none; box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2); }
            .image-hider-modal .shortcut-input.recording { background: #e8f5e9; }
            .image-hider-modal .shortcut-input::placeholder { color: #999; }
            .image-hider-modal .button-wrapper { display: flex; justify-content: center; }
            .image-hider-modal button { padding: 9px 18px; border-radius: 6px; border: 1px solid transparent; cursor: pointer; font-size: 0.95em; font-weight: 500; transition: background-color 0.2s, border-color 0.2s, color 0.2s; min-width: 110px; height: 38px; line-height: 1.5; display: inline-flex; align-items: center; justify-content: center; margin: 0; }
            .image-hider-modal button:disabled { cursor: not-allowed; opacity: 0.7; }
            .image-hider-modal #setSingleShortcutBtn, .image-hider-modal #setGlobalShortcutBtn { background-color: #f0f0f0; color: #333; border-color: #ccc; width: auto; }
            .image-hider-modal #setSingleShortcutBtn:hover:not(:disabled), .image-hider-modal #setGlobalShortcutBtn:hover:not(:disabled) { background-color: #e0e0e0; border-color: #bbb; }
            .image-hider-modal .button-group { display: flex; gap: 12px; margin-top: 24px; justify-content: center; }
            .image-hider-modal #saveConfigBtn { background-color: #4CAF50; color: white; min-width: 140px; font-size: 1em; }
            .image-hider-modal #saveConfigBtn:hover { background-color: #45a049; }
            .image-hider-modal .close-btn { position: absolute; right: 12px; top: 12px; width: 30px; height: 30px; border-radius: 50%; background: #f1f1f1; border: none; cursor: pointer; transition: background-color 0.2s, transform 0.2s; padding: 0; min-width: auto; display: flex; align-items: center; justify-content: center; color: #666; }
            .image-hider-modal .close-btn:hover { background: #e0e0e0; transform: rotate(90deg); }
            .image-hider-modal .close-btn::before, .image-hider-modal .close-btn::after { content: ''; position: absolute; width: 14px; height: 2px; background: currentColor; border-radius: 1px; }
            .image-hider-modal .close-btn::before { transform: rotate(45deg); }
            .image-hider-modal .close-btn::after { transform: rotate(-45deg); }
            .image-hider-modal .hint { color: #666; font-size: 0.85em; margin-top: 20px; line-height: 1.5; padding: 12px; background: #f9f9f9; border-radius: 6px; border: 1px solid #eee; }
            .image-hider-modal .success-message { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: rgba(76, 175, 80, 0.9); color: white; padding: 10px 20px; border-radius: 6px; font-weight: 500; opacity: 0; transition: opacity 0.3s ease; pointer-events: none; z-index: 10001; }
            .image-hider-modal .success-message.show { opacity: 1; }
        `);
        const styleElement = document.querySelector(`style[id="${CSS_MODAL_ID}"]`);
        if (styleElement) styleElement.id = CSS_MODAL_ID;
    }

    function initialize() {
        injectGlobalStyles();
        document.addEventListener('keydown', handleKeyDown, { capture: true });
        document.addEventListener('keyup', handleKeyUp, { capture: true });
        document.addEventListener('mousedown', handleMouseDown, { capture: true });
        document.addEventListener('mouseup', handleMouseUp, { capture: true });
        document.addEventListener('mouseover', handleMouseOver, { capture: false });
        document.addEventListener('mouseout', handleMouseOut, { capture: false });
        window.addEventListener('beforeunload', resetStateOnUnload);
        window.addEventListener('blur', () => currentPressedKeys.clear());
        GM_registerMenuCommand('配置快捷键', createConfigModal);
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initAutoHideAndObserver);
        } else {
            initAutoHideAndObserver();
        }
    }

    initialize();

})();