NodeSeek Plugin

NodeSeek插件: 图床粘贴

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         NodeSeek Plugin
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  NodeSeek插件: 图床粘贴
// @license      GPL License
// @author       beibiele
// @match        https://www.nodeseek.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

(function() {
    'use strict';

    // 常量定义
    const CONFIG = {
        STORAGE_KEY: 'nodeseek_plus_config',
        API_LIST: [
            {
                id: '111666',
                name: '111666图床'
            },
            {
                id: 'uhsea',
                name: '屋舍图床'
            }
        ]
    };

    // 默认配置
    const defaultConfig = {
        apiId: CONFIG.API_LIST[0].id
    };

    // 当前选择的图床 ID(用于缓存)
    let currentApiId = null;

    // 获取当前配置
    function getConfig() {
        const config = GM_getValue(CONFIG.STORAGE_KEY, defaultConfig);
        // 更新缓存
        currentApiId = config.apiId;
        return config;
    }

    // 保存配置
    function saveConfig(config) {
        GM_setValue(CONFIG.STORAGE_KEY, config);
        // 更新缓存
        currentApiId = config.apiId;
    }

    // 显示配置界面
    function showConfigDialog() {
        const config = getConfig();
        const currentApi = CONFIG.API_LIST.find(api => api.id === config.apiId) || CONFIG.API_LIST[0];

        // 创建对话框
        const dialog = document.createElement('div');
        dialog.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            z-index: 10000;
            font-family: Arial, sans-serif;
        `;

        // 创建标题
        const title = document.createElement('h3');
        title.textContent = '选择图床';
        title.style.margin = '0 0 15px 0';
        dialog.appendChild(title);

        // 创建下拉选择框
        const select = document.createElement('select');
        select.style.cssText = `
            width: 100%;
            padding: 8px;
            margin-bottom: 15px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
        `;

        // 添加选项
        CONFIG.API_LIST.forEach(api => {
            const option = document.createElement('option');
            option.value = api.id;
            option.textContent = api.name;
            if (api.id === currentApi.id) {
                option.selected = true;
            }
            select.appendChild(option);
        });

        dialog.appendChild(select);

        // 创建按钮容器
        const buttonContainer = document.createElement('div');
        buttonContainer.style.cssText = `
            display: flex;
            justify-content: flex-end;
            gap: 10px;
        `;

        // 创建取消按钮
        const cancelButton = document.createElement('button');
        cancelButton.textContent = '取消';
        cancelButton.style.cssText = `
            padding: 8px 15px;
            border: 1px solid #ddd;
            border-radius: 4px;
            background: #f5f5f5;
            cursor: pointer;
        `;
        cancelButton.onclick = () => {
            document.body.removeChild(dialog);
            document.body.removeChild(overlay);
        };

        // 创建确定按钮
        const confirmButton = document.createElement('button');
        confirmButton.textContent = '确定';
        confirmButton.style.cssText = `
            padding: 8px 15px;
            border: none;
            border-radius: 4px;
            background: #4CAF50;
            color: white;
            cursor: pointer;
        `;
        confirmButton.onclick = () => {
            const newApiId = select.value;
            saveConfig({
                ...config,
                apiId: newApiId
            });
            const newApi = CONFIG.API_LIST.find(api => api.id === newApiId);
            alert(`已切换到:${newApi.name},需要重新复制图片哦~`);
            document.body.removeChild(dialog);
            document.body.removeChild(overlay);

            // 重新注册菜单命令以更新显示
            try {
                GM_unregisterMenuCommand("当前图床: " + currentApi.name);
                initMonkeyMenu();
            } catch (e) {
                console.error("无法更新菜单");
            }
        };

        buttonContainer.appendChild(cancelButton);
        buttonContainer.appendChild(confirmButton);
        dialog.appendChild(buttonContainer);

        // 创建遮罩层
        const overlay = document.createElement('div');
        overlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            z-index: 9999;
        `;

        // 添加到页面
        document.body.appendChild(overlay);
        document.body.appendChild(dialog);
    }

    // 初始化脚本菜单
    function initMonkeyMenu() {
        try {
            const config = getConfig();
            const currentApi = CONFIG.API_LIST.find(api => api.id === config.apiId) || CONFIG.API_LIST[0];
            GM_registerMenuCommand(`当前图床: ${currentApi.name}`, showConfigDialog);
        } catch (e) {
            console.error("无法使用Tampermonkey");
        }
    }

    // 初始化菜单和配置
    initMonkeyMenu();
    getConfig(); // 初始化当前图床 ID

    document.addEventListener('keyup', doc_keyUp, false);
    async function doc_keyUp(event) {
        if (event.ctrlKey && event.code == "KeyV") {
            handlePaste();
        }
    }

    async function handlePaste() {
        let imageFiles = [];
        const clipboardItems = await navigator.clipboard.read();
        for (const clipboardItem of clipboardItems) {
            for (const type of clipboardItem.types) {
                if (type.indexOf('image/') !== -1) {
                    let blob = await clipboardItem.getType(type);
                    imageFiles.push(blob);
                }
            }
        }

        if (imageFiles.length > 0) {
            for (let file of imageFiles) {
                await uploadToImageHost(file);
            }
        }
    }

    // 111666图床上传函数
    function upload_111666(file) {
        const apiUrl = 'https://i.111666.best';
        const uploadEndpoint = '/image';

        return new Promise((resolve, reject) => {
            const formData = new FormData();
            formData.append('file', file);

            GM_xmlhttpRequest({
                method: 'POST',
                url: apiUrl + uploadEndpoint,
                data: formData,
                headers: {
                    'Auth-Token': crypto.randomUUID()
                },
                onload: (response) => {
                    let jsonResponse = JSON.parse(response.responseText);
                    if (response.status === 200 && jsonResponse && jsonResponse.ok) {
                        insertToEditor(apiUrl + jsonResponse.src);
                    } else {
                        mscAlert('图片上传成功,但未获取到Markdown链接');
                    }
                    resolve();
                },
                onerror: (error) => {
                    mscAlert('图片上传遇到错误,请检查网络或稍后重试。');
                    reject(error);
                }
            });
        });
    }

    // 屋舍图床上传函数
    function upload_uhsea(file) {
        const apiUrl = 'https://uhsea.com/Frontend/upload';

        return new Promise((resolve, reject) => {
            const formData = new FormData();
            formData.append('file', file);

            GM_xmlhttpRequest({
                method: 'POST',
                url: apiUrl,
                data: formData,
                onload: (response) => {
                    let jsonResponse = JSON.parse(response.responseText);
                    if (response.status === 200 && jsonResponse && jsonResponse.data) {
                        insertToEditor(jsonResponse.data);
                    } else {
                        mscAlert('图片上传成功,但未获取到Markdown链接');
                    }
                    resolve();
                },
                onerror: (error) => {
                    mscAlert('图片上传遇到错误,请检查网络或稍后重试。');
                    reject(error);
                }
            });
        });
    }

    // 根据当前选择的图床调用对应的上传函数
    function uploadToImageHost(file) {
        // 使用缓存的图床 ID
        const apiId = currentApiId || defaultConfig.apiId;
        const apiConfig = CONFIG.API_LIST.find(api => api.id === apiId);

        // 如果没有找到配置,使用默认图床
        if (!apiConfig) {
            console.warn('未找到对应的图床配置,使用默认图床');
            return upload_111666(file);
        }

        switch (apiConfig.id) {
            case '111666':
                return upload_111666(file);
            case 'uhsea':
                return upload_uhsea(file);
            default:
                console.warn('不支持的图床,使用默认图床');
                return upload_111666(file);
        }
    }

    function insertToEditor(Link) {
        const codeMirrorElement = document.querySelector('.CodeMirror');
        if (codeMirrorElement) {
            const codeMirrorInstance = codeMirrorElement.CodeMirror;
            if (codeMirrorInstance) {
                const cursor = codeMirrorInstance.getCursor();
                let markdownLink = '![](' + Link + ')';
                GM_setClipboard(markdownLink);
                codeMirrorInstance.replaceRange(markdownLink + '\n', cursor);
            }
        }
    }
})();