Greasy Fork

Greasy Fork is available in English.

Pinterest Ultra Assistant V6.4 (Vivid Color Engine)

Bilingual UI, AI 2x Sharpen, Source Finder, and VIVID Color Palette (Prioritizes accent colors like green patina).

当前为 2025-12-30 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Pinterest Ultra Assistant V6.4 (Vivid Color Engine)
// @namespace    http://tampermonkey.net/
// @version      6.4
// @description  Bilingual UI, AI 2x Sharpen, Source Finder, and VIVID Color Palette (Prioritizes accent colors like green patina).
// @author       Pi Xiao
// @match        https://*.pinterest.com/*
// @grant        GM_openInTab
// @grant        GM_setClipboard
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const BRIDGE_PAGE = "https://meishubiji.cn/ai-processing-center/";

    // --- 算法:颜色感知差异度 ---
    function getColorDist(hex1, hex2) {
        const r1 = parseInt(hex1.slice(1,3), 16), g1 = parseInt(hex1.slice(3,5), 16), b1 = parseInt(hex1.slice(5,7), 16);
        const r2 = parseInt(hex2.slice(1,3), 16), g2 = parseInt(hex2.slice(3,5), 16), b2 = parseInt(hex2.slice(5,7), 16);
        // 使用简单的加权欧式距离,更符合人眼对色彩的感知
        return Math.sqrt((r1-r2)**2 * 0.3 + (g1-g2)**2 * 0.59 + (b1-b2)**2 * 0.11);
    }

    function rgbToHex(r, g, b) {
        return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
    }

    // --- 核心:提取灵魂色盘 ---
    function getVividPalette(img) {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        canvas.width = 100; canvas.height = 100; // 提高采样精度
        ctx.drawImage(img, 0, 0, 100, 100);
        
        const data = ctx.getImageData(0, 0, 100, 100).data;
        let colorMap = {};
        
        for (let i = 0; i < data.length; i += 12) { // 步进采样
            const r = data[i], g = data[i+1], b = data[i+2], a = data[i+3];
            if (a < 128) continue;
            
            const max = Math.max(r, g, b), min = Math.min(r, g, b);
            const saturation = max - min; // 饱和度计算
            const brightness = (max + min) / 2;
            
            // 核心逻辑:如果颜色太暗(接近黑)或太亮(接近白)或太灰(低饱和度),则大幅扣分
            let bias = 1;
            if (saturation < 30) bias = 0.1; // 灰色区域权重极低
            if (brightness < 40 || brightness > 220) bias = 0.2; // 极暗或极亮权重降低

            const hex = rgbToHex(r, g, b);
            // 权重 = 出现频率 * 饱和度的平方 * 偏差修正
            colorMap[hex] = (colorMap[hex] || 0) + (Math.pow(saturation, 2) * bias + 1);
        }

        const sortedColors = Object.keys(colorMap).sort((a, b) => colorMap[b] - colorMap[a]);
        const finalPalette = [];
        
        for (const color of sortedColors) {
            if (finalPalette.length >= 6) break;
            // 确保颜色多样性:新颜色必须与已有颜色有显著差异
            if (finalPalette.every(c => getColorDist(c, color) > 50)) {
                finalPalette.push(color);
            }
        }
        
        // 如果抓出来的颜色不足6个(比如纯色图),再用普通排序补齐
        if (finalPalette.length < 6) {
            for (const color of sortedColors) {
                if (finalPalette.length >= 6) break;
                if (!finalPalette.includes(color)) finalPalette.push(color);
            }
        }
        return finalPalette;
    }

    // --- 智能预览窗 ---
    async function processAndShow(imgUrl) {
        const originalUrl = imgUrl.replace(/\/(236x|474x|564x|736x|1200x)\//, '/originals/').replace(/\.webp$/, '.jpg');
        const overlay = document.createElement('div');
        overlay.id = "px-overlay";
        overlay.style = "position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.97);z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;color:white;font-family:sans-serif;cursor:zoom-out;";
        overlay.innerHTML = '<div style="text-align:center;"><div class="px-spinner"></div><div style="margin-top:15px; font-size:14px; color:#00ffcc; letter-spacing:1px; font-weight:bold;">EXTRACTING SOUL COLORS...</div></div><style>.px-spinner { width:35px; height:35px; border:3px solid rgba(0,255,204,0.1); border-top-color:#00ffcc; border-radius:50%; animation:spin 0.8s linear infinite; margin:0 auto; } @keyframes spin { to { transform:rotate(360deg); } } .px-color:hover { transform: translateY(-5px); box-shadow: 0 5px 15px rgba(255,255,255,0.3); }</style>';
        overlay.onclick = () => overlay.remove();
        document.body.appendChild(overlay);

        const img = new Image();
        img.crossOrigin = "Anonymous";
        img.src = originalUrl;
        
        img.onload = function() {
            const palette = getVividPalette(img);
            overlay.innerHTML = "";
            const container = document.createElement('div');
            container.style = "text-align:center; width:95%; height:90vh; display:flex; flex-direction:column; cursor:default;";
            container.onclick = (e) => e.stopPropagation();
            
            const scrollBox = document.createElement('div');
            scrollBox.style = "overflow:auto; border:1px solid #333; border-radius:12px; flex:1; background:#050505; display:flex; align-items:center; justify-content:center; box-shadow: inset 0 0 100px rgba(0,0,0,1);";
            const pImg = document.createElement('img');
            pImg.src = originalUrl;
            pImg.style = "max-width:200%; image-rendering:-webkit-optimize-contrast; filter:contrast(1.08) saturate(1.05);";
            scrollBox.appendChild(pImg);

            const bar = document.createElement('div');
            bar.style = "padding:30px; display:flex; flex-direction:column; align-items:center; gap:25px; background:#0a0a0a;";
            
            // 色盘生成
            let paletteHTML = `<div style="display:flex; gap:15px; align-items:center;">
                <span style="font-size:11px; color:#444; text-transform:uppercase; letter-spacing:2px; font-weight:bold;">The Soul Palette:</span>`;
            palette.forEach(color => {
                paletteHTML += `<div class="px-color" style="width:32px; height:32px; background:${color}; border-radius:8px; cursor:pointer; border:2px solid #222; transition:0.3s;" title="Copy Hex: ${color}" data-hex="${color}"></div>`;
            });
            paletteHTML += `</div>`;
            
            const actionHTML = `
                <div style="display:flex; gap:25px; align-items:center;">
                    <button id="btn-ai-go" style="background:linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); color:white; border:none; padding:16px 50px; border-radius:50px; font-size:16px; font-weight:bold; cursor:pointer; box-shadow:0 10px 30px rgba(37,117,252,0.4); transition:0.3s;">🚀 LAUNCH AI 8K ENGINE</button>
                    <span style="color:#333; cursor:pointer; font-size:12px; font-weight:bold;" onclick="document.getElementById('px-overlay').remove()">CLOSE PREVIEW ×</span>
                </div>
            `;

            bar.innerHTML = paletteHTML + actionHTML;
            container.appendChild(scrollBox);
            container.appendChild(bar);
            overlay.appendChild(container);

            overlay.querySelectorAll('.px-color').forEach(el => {
                el.onclick = () => {
                    const hex = el.getAttribute('data-hex');
                    GM_setClipboard(hex);
                    el.style.borderColor = "#fff";
                    setTimeout(() => el.style.borderColor = "#222", 500);
                };
            });
            overlay.querySelector('#btn-ai-go').onclick = () => window.open(`${BRIDGE_PAGE}?url=${encodeURIComponent(originalUrl)}`, '_blank');
        };
        img.onerror = () => { window.open(originalUrl, '_blank'); overlay.remove(); };
    }

    // --- 注入逻辑 ---
    function injectButtons() {
        const images = document.querySelectorAll('img');
        images.forEach(img => {
            const src = img.src;
            if (!src || !src.includes('pinimg.com') || img.width < 150 || img.closest('.px-helper-bar')) return;
            const container = img.closest('[data-test-id="pin-visual-wrapper"]') || img.closest('[data-test-id="visual-content-container"]') || img.parentElement;
            if (container) {
                if (window.getComputedStyle(container).position === 'static') container.style.position = 'relative';
                const bar = document.createElement('div');
                bar.className = 'px-helper-bar';
                bar.style = "position:absolute; top:10px; left:10px; z-index:1000; display:flex; gap:4px; opacity:0; transition:0.3s; pointer-events:auto;";
                container.addEventListener('mouseenter', () => bar.style.opacity = "1");
                container.addEventListener('mouseleave', () => bar.style.opacity = "0");
                const btnStyle = 'color:white; border:none; border-radius:4px; cursor:pointer; padding:4px 8px; font-weight:bold; font-size:9px; box-shadow:0 2px 5px rgba(0,0,0,0.3); white-space:nowrap;';
                const b1 = document.createElement('button'); b1.innerHTML = '🪄 2x HD'; b1.style = btnStyle + 'background:#00BFFF;';
                b1.onclick = (e) => { e.preventDefault(); e.stopPropagation(); processAndShow(src); };
                const b2 = document.createElement('button'); b2.innerHTML = '🖼️ Originals'; b2.style = btnStyle + 'background:#E60023;';
                b2.onclick = (e) => { e.preventDefault(); e.stopPropagation(); window.open(src.replace(/\/(236x|474x|564x|736x)\//, '/originals/'), '_blank'); };
                const b3 = document.createElement('button'); b3.innerHTML = '🔍 Source'; b3.style = btnStyle + 'background:#34a853;';
                b3.onclick = (e) => { e.preventDefault(); e.stopPropagation(); window.open(`https://lens.google.com/uploadbyurl?url=${encodeURIComponent(src.replace(/\/(236x|474x|564x|736x)\//, '/originals/'))}`, '_blank'); };
                bar.appendChild(b1); bar.appendChild(b2); bar.appendChild(b3);
                container.appendChild(bar);
            }
        });
    }

    setInterval(injectButtons, 2500);
    const observer = new MutationObserver(injectButtons);
    observer.observe(document.body, { childList: true, subtree: true });
})();