// ==UserScript== // @name 牛牛聊天发图片插件 // @namespace https://www.milkywayidle.com/ // @version 0.1.0 // @description https://www.milkywayidle.com/favicon.svg // @match https://www.milkywayidle.com/* // @match https://test.milkywayidle.com/* // @grant GM_addStyle // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; GM_addStyle(` .chat-img { display: inline-flex; margin: 1px 4px; max-height: 60px; max-width: 100px; width: fit-content; border: 2px solid #778be1; border-radius: 4px; padding: 1px; white-space: nowrap; background: #000; cursor: pointer; } .chat-img-preview { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: flex; justify-content: center; align-items: center; z-index: 9999; cursor: zoom-out; } .chat-img-preview img { max-width: 90%; max-height: 90%; border: 2px solid #fff; border-radius: 4px; } `); const chatHistorySelector = 'div.ChatHistory_chatHistory__1EiG3'; const chatMessageSelector = 'div.ChatMessage_chatMessage__2wev4'; function isImageUrl(url) { return url && /\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?.*)?$/i.test(url); } function createPreviewOverlay(imgSrc) { const overlay = document.createElement('div'); overlay.className = 'chat-img-preview'; const previewImg = document.createElement('img'); previewImg.src = imgSrc; overlay.appendChild(previewImg); document.body.appendChild(overlay); overlay.addEventListener('click', (e) => { if (e.target === overlay || e.target === previewImg) { document.body.removeChild(overlay); } }); document.addEventListener('keydown', function handleEsc(e) { if (e.key === 'Escape') { document.body.removeChild(overlay); document.removeEventListener('keydown', handleEsc); } }); } function replaceLinkContentWithImage(link) { const href = link.getAttribute('href'); if (!isImageUrl(href)) return; if (link.querySelector('.chat-img')) return; link.innerHTML = ''; const img = document.createElement('img'); img.src = href; img.className = 'chat-img'; link.appendChild(img); link.addEventListener('click', (e) => { e.preventDefault(); createPreviewOverlay(href); }); } function processExistingMessages(container) { const messages = container.querySelectorAll(chatMessageSelector); messages.forEach(msg => { const links = msg.querySelectorAll('a'); links.forEach(replaceLinkContentWithImage); }); } function observeChatHistory(chatHistory) { processExistingMessages(chatHistory); const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { const messages = node.matches(chatMessageSelector) ? [node] : node.querySelectorAll(chatMessageSelector); messages.forEach(msg => { const links = msg.querySelectorAll('a'); links.forEach(replaceLinkContentWithImage); }); } }); }); }); observer.observe(chatHistory, { childList: true, subtree: true }); } function init() { const chatHistories = document.querySelectorAll(chatHistorySelector); if (chatHistories.length === 0) { setTimeout(init, 1000); return; } chatHistories.forEach(observeChatHistory); const globalObserver = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { const newHistories = node.querySelectorAll?.(chatHistorySelector) || []; newHistories.forEach(observeChatHistory); } }); }); }); globalObserver.observe(document.body, { childList: true, subtree: true }); } init(); })();