Pinterest Quick Pin

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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);
    }
})();