Pinterest Quick Pin

使用Ctrl+V自动从剪贴板中上传Pin图

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Pinterest Quick Pin
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  使用Ctrl+V自动从剪贴板中上传Pin图
// @author       Tz
// @license      MIT
// @match        https://*.pinterest.com/*
// @match        https://pinterest.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=pinterest.com
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // === 配置常量 ===
    const CFG = {
        uploadUrl: 'https://www.pinterest.com/pin-creation-tool/',
        key: 'gemini_pin_cache',
        width: '1500px' // 上传窗口宽度
    };

    // === 通用工具函数 ===
    const setStyle = (el, styles) => Object.assign(el.style, styles);

    // 黑白纯文本提示框
    const showToast = (msg) => {
        let t = document.getElementById('g-toast');
        if (!t) {
            t = document.createElement('div');
            t.id = 'g-toast';
            setStyle(t, {
                position: 'fixed', top: '20px', left: '50%', transform: 'translateX(-50%)',
                background: 'rgba(0,0,0,0.85)', color: '#fff', padding: '8px 20px',
                borderRadius: '4px', zIndex: '9999999', fontSize: '14px', pointerEvents: 'none',
                transition: 'opacity 0.2s', opacity: '0', fontFamily: 'sans-serif'
            });
            document.body.appendChild(t);
        }
        t.innerText = msg;
        t.style.opacity = '1';
        clearTimeout(t.timer);
        t.timer = setTimeout(() => { t.style.opacity = '0'; }, 2000);
    };

    // =================================================================
    // 逻辑 A:主页面 (监听 Ctrl+V)
    // =================================================================
    if (window.top === window.self) {
        let modal, iframe;

        // 1. 状态指示灯 (左下角小灰点)
        const dot = document.createElement('div');
        setStyle(dot, {
            position: 'fixed', bottom: '10px', left: '10px', width: '6px', height: '6px',
            borderRadius: '50%', background: '#999', zIndex: '9999', opacity: '0.4', pointerEvents: 'none'
        });
        document.body.appendChild(dot);

        // 2. 弹出上传窗口
        const openModal = () => {
            if (!modal) {
                // 背景遮罩
                modal = document.createElement('div');
                setStyle(modal, {
                    position: 'fixed', top: '0', left: '0', width: '100vw', height: '100vh',
                    background: 'rgba(0,0,0,0.75)', zIndex: '999999', display: 'flex',
                    justifyContent: 'center', alignItems: 'center'
                });

                // 容器
                const box = document.createElement('div');
                setStyle(box, {
                    width: CFG.width, maxWidth: '98vw', height: '92vh', background: '#fff',
                    borderRadius: '20px', overflow: 'hidden', position: 'relative'
                });

                // Iframe
                iframe = document.createElement('iframe');
                iframe.src = CFG.uploadUrl;
                setStyle(iframe, { width: '100%', height: '100%', border: 'none' });

                box.appendChild(iframe);
                modal.appendChild(box);
                document.body.appendChild(modal);

                // 点击背景关闭
                modal.onclick = (e) => { if(e.target === modal) modal.style.display = 'none'; };
            }
            modal.style.display = 'flex';
            // 强制刷新 iframe 以触发自动填入逻辑
            if (iframe.contentWindow.location.href !== 'about:blank') iframe.contentWindow.location.reload();
        };

        // 3. 监听粘贴事件
        window.addEventListener('paste', (e) => {
            const tag = document.activeElement.tagName;
            if (['INPUT', 'TEXTAREA'].includes(tag) || document.activeElement.isContentEditable) return;

            const item = Array.from(e.clipboardData.items).find(i => i.kind === 'file' && i.type.startsWith('image/'));

            if (item) {
                e.preventDefault();
                const reader = new FileReader();
                reader.onload = () => {
                    try {
                        localStorage.setItem(CFG.key, reader.result);
                        openModal(); // 打开弹窗
                    } catch (err) { showToast('图片过大,无法缓存'); }
                };
                reader.readAsDataURL(item.getAsFile());
            } else {
                showToast('剪贴板中无图片');
            }
        });

        // 4. 监听 ESC 键退出
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && modal) modal.style.display = 'none';
        });
    }

    // =================================================================
    // 逻辑 B:Iframe 内部 (自动填入)
    // =================================================================
    else if (window.location.href.includes('pin-creation-tool') || window.location.href.includes('pin-builder')) {

        //隐藏滚动条
        const style = document.createElement('style');
        style.innerHTML = `body { overflow: hidden !important; }`; // 强制隐藏 body 的滚动条
        document.head.appendChild(style);

        //轮询检测并填入
        const timer = setInterval(() => {
            const b64 = localStorage.getItem(CFG.key);
            const input = document.querySelector('input[type="file"]');

            if (b64 && input) {
                clearInterval(timer);
                // Base64 转 File
                const arr = b64.split(','), bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
                for (let i = 0; i < n; i++) u8arr[i] = bstr.charCodeAt(i);
                const file = new File([u8arr], "pasted_image.png", { type: arr[0].match(/:(.*?);/)[1] });

                // 触发上传
                const dt = new DataTransfer(); dt.items.add(file);
                input.files = dt.files;
                input.dispatchEvent(new Event('change', { bubbles: true }));

                localStorage.removeItem(CFG.key); // 清除缓存
                showToast('图片已自动填入');
            }
        }, 300);
    }
})();