// ==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();
})();