Greasy Fork

Greasy Fork is available in English.

牛牛聊天发图片插件

https://www.milkywayidle.com/favicon.svg

目前为 2025-05-12 提交的版本。查看 最新版本

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