Web3 OKX Color Theme Changer

Automaticly color changer, Written by ai

当前为 2025-09-28 提交的版本,查看 最新版本

// ==UserScript==
// @name         Web3 OKX Color Theme Changer
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Automaticly color changer, Written by ai
// @author       mamiis
// @match        https://*.okx.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function(){
    'use strict';

    const UP_COLOR = {r:20,  g:167, b:255};   // hedef mavi
    const DOWN_COLOR = {r:126,g:87,  b:194};  // hedef mor
    const TRANSFORM_ALPHA = true;

    // Geliştirilmiş grafik filtre ayarları
    const CHART_FILTERS = {
        green: {
            hue: 210,    // Mavi tonu
            saturation: 1.6,
            brightness: 1.05
        },
        red: {
            hue: 280,    // Mor tonu
            saturation: 1.3,
            brightness: 1.1
        }
    };

    // Regex'ler
    const hexRegex = /#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b/g;
    const rgbRegex = /rgba?\([^\)]+\)/gi;
    const hslRegex = /hsla?\([^\)]+\)/gi;

    // yardımcı DOM elemanı
    const _tmp = document.createElement('div');
    _tmp.style.position = 'absolute';
    _tmp.style.left = '-9999px';
    _tmp.style.width = _tmp.style.height = '1px';
    document.documentElement.appendChild(_tmp);

    /* ---------- renk parse / convert yardımcıları ---------- */

    function clamp(v, a=0, b=255){ return Math.max(a, Math.min(b, v)); }

    function parseHexColor(str){
        let s = str.replace('#','');
        if(s.length === 3){
            s = s.split('').map(ch => ch+ch).join('');
        } else if(s.length === 4){
            s = s.split('').map(ch => ch+ch).join('');
        }
        let r=0,g=0,b=0,a=1;
        if(s.length === 6){
            r = parseInt(s.substr(0,2),16);
            g = parseInt(s.substr(2,2),16);
            b = parseInt(s.substr(4,2),16);
        } else if(s.length === 8){
            r = parseInt(s.substr(0,2),16);
            g = parseInt(s.substr(2,2),16);
            b = parseInt(s.substr(4,2),16);
            a = parseInt(s.substr(6,2),16) / 255;
        }
        return {r,g,b,a};
    }

    function parseRgbString(str){
        const nums = str.match(/[\d\.%]+/g);
        if(!nums) return null;
        let [r,g,b,a] = [0,0,0,1];
        if(nums.length >= 3){
            const parseComponent = (v) => {
                if(v.endsWith('%')) return Math.round(parseFloat(v) * 2.55);
                return Math.round(parseFloat(v));
            };
            r = parseComponent(nums[0]);
            g = parseComponent(nums[1]);
            b = parseComponent(nums[2]);
        }
        if(nums.length >= 4) a = parseFloat(nums[3]);
        return {r,g,b,a};
    }

    function parseHslString(str){
        const parts = str.match(/[\d\.%]+/g);
        if(!parts) return null;
        let h = parseFloat(parts[0]);
        let s = parseFloat(parts[1]) / 100;
        let l = parseFloat(parts[2]) / 100;
        let a = parts.length >= 4 ? parseFloat(parts[3]) : 1;
        const rgb = hslToRgb(h, s, l);
        return {r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b), a};
    }

    function hslToRgb(h, s, l){
        h = ((h % 360) + 360) % 360;
        const c = (1 - Math.abs(2*l - 1)) * s;
        const x = c * (1 - Math.abs((h / 60) % 2 - 1));
        const m = l - c/2;
        let r=0,g=0,b=0;
        if(h < 60) { r=c; g=x; b=0; }
        else if(h < 120) { r=x; g=c; b=0; }
        else if(h < 180) { r=0; g=c; b=x; }
        else if(h < 240) { r=0; g=x; b=c; }
        else if(h < 300) { r=x; g=0; b=c; }
        else { r=c; g=0; b=x; }
        return { r: (r+m)*255, g: (g+m)*255, b: (b+m)*255 };
    }

    function rgbToHsl(r,g,b){
        r/=255; g/=255; b/=255;
        const max = Math.max(r,g,b), min = Math.min(r,g,b);
        let h=0,s=0,l=(max+min)/2;
        if(max !== min){
            const d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
            switch(max){
                case r: h = ((g - b) / d + (g < b ? 6 : 0)) * 60; break;
                case g: h = ((b - r) / d + 2) * 60; break;
                case b: h = ((r - g) / d + 4) * 60; break;
            }
        }
        return {h, s, l};
    }

    function parseColorString(str){
        if(!str) return null;
        str = str.trim();
        if(str.toLowerCase() === 'transparent' || str === 'none') return {r:0,g:0,b:0,a:0};
        try{
            if(str[0] === '#') return parseHexColor(str);
            if(str.toLowerCase().startsWith('rgb')) return parseRgbString(str);
            if(str.toLowerCase().startsWith('hsl')) return parseHslString(str);
            _tmp.style.color = '';
            _tmp.style.color = str;
            const cs = getComputedStyle(_tmp).color;
            return parseRgbString(cs);
        }catch(e){
            return null;
        }
    }

    function rgbaToCss(o){
        const a = (typeof o.a === 'number') ? o.a : 1;
        return `rgba(${Math.round(o.r)}, ${Math.round(o.g)}, ${Math.round(o.b)}, ${+a.toFixed(3)})`;
    }

    /* ---------- renk tespit mantığı ---------- */

    function shouldConvertByHue(rgba){
        if(!rgba) return null;
        if(rgba.a === 0) return null;
        const {h,s,l} = rgbToHsl(rgba.r, rgba.g, rgba.b);
        if(s > 0.08 && h >= 70 && h <= 170) return 'greenish';
        if(s > 0.08 && (h <= 25 || h >= 335)) return 'reddish';
        return null;
    }

    function convertBasedOnDetection(rgba){
        const det = shouldConvertByHue(rgba);
        if(!det) return rgba;

        let {h, s, l} = rgbToHsl(rgba.r, rgba.g, rgba.b);

        if(det === 'greenish'){
            h = CHART_FILTERS.green.hue;
            s *= CHART_FILTERS.green.saturation;
            l *= CHART_FILTERS.green.brightness;
        }
        if(det === 'reddish'){
            h = CHART_FILTERS.red.hue;
            s *= CHART_FILTERS.red.saturation;
            l *= CHART_FILTERS.red.brightness;
        }

        s = clamp(s, 0, 1);
        l = clamp(l, 0, 1);

        const rgb = hslToRgb(h, s, l);
        return {r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b), a: rgba.a};
    }

    /* ---------- text içindeki renk tokenlarını değiştir ---------- */

    function replaceColorTokensInText(text){
        if(!text || typeof text !== 'string') return text;
        let out = text;

        out = out.replace(hexRegex, (m) => {
            const p = parseColorString(m);
            if(!p) return m;
            const conv = convertBasedOnDetection(p);
            if(conv && (conv.r !== p.r || conv.g !== p.g || conv.b !== p.b || conv.a !== p.a)){
                return rgbaToCss(conv);
            }
            return m;
        });

        out = out.replace(rgbRegex, (m) => {
            const p = parseColorString(m);
            if(!p) return m;
            const conv = convertBasedOnDetection(p);
            if(conv && (conv.r !== p.r || conv.g !== p.g || conv.b !== p.b || conv.a !== p.a)){
                return rgbaToCss(conv);
            }
            return m;
        });

        out = out.replace(hslRegex, (m) => {
            const p = parseColorString(m);
            if(!p) return m;
            const conv = convertBasedOnDetection(p);
            if(conv && (conv.r !== p.r || conv.g !== p.g || conv.b !== p.b || conv.a !== p.a)){
                return rgbaToCss(conv);
            }
            return m;
        });

        return out;
    }

    /* ---------- stylesheet / style tag işleme ---------- */

    function processCssRuleList(rules){
        for(let i=0;i<rules.length;i++){
            const rule = rules[i];
            try{
                if(rule.type === CSSRule.STYLE_RULE){
                    const style = rule.style;
                    for(let j=0;j<style.length;j++){
                        const prop = style[j];
                        const val = style.getPropertyValue(prop);
                        const newVal = replaceColorTokensInText(val);
                        if(newVal !== val){
                            try{ style.setProperty(prop, newVal, style.getPropertyPriority(prop)); }catch(e){}
                        }
                    }
                } else if(rule.cssRules && rule.cssRules.length){
                    processCssRuleList(rule.cssRules);
                }
            }catch(e){
                continue;
            }
        }
    }

    function processStyleSheets(){
        for(const sheet of document.styleSheets){
            try{
                if(!sheet.cssRules) continue;
                processCssRuleList(sheet.cssRules);
            }catch(e){
                continue;
            }
        }
    }

    function processStyleTags(){
        document.querySelectorAll('style').forEach(tag=>{
            try{
                const old = tag.textContent;
                const neu = replaceColorTokensInText(old);
                if(neu !== old) tag.textContent = neu;
            }catch(e){}
        });
    }

    /* ---------- inline attrs ve SVG fill/stroke ---------- */

    function processInlineAndSvg(root=document){
        root.querySelectorAll('[style]').forEach(el=>{
            try{
                const old = el.getAttribute('style');
                const neu = replaceColorTokensInText(old);
                if(neu !== old) el.setAttribute('style', neu);
            }catch(e){}
        });

        root.querySelectorAll('[fill],[stroke]').forEach(el=>{
            ['fill','stroke'].forEach(attr=>{
                try{
                    if(el.hasAttribute(attr)){
                        const old = el.getAttribute(attr);
                        const neu = replaceColorTokensInText(old);
                        if(neu !== old) el.setAttribute(attr, neu);
                    }
                }catch(e){}
            });
        });
    }

    /* ---------- computed styles ---------- */

    const computedCheckProps = [
        'background-color','color',
        'border-top-color','border-right-color','border-bottom-color','border-left-color',
        'outline-color','caret-color','column-rule-color'
    ];

    function isTransparentValue(v){
        if(!v) return true;
        v = v.trim();
        return v === 'transparent' || v === 'rgba(0, 0, 0, 0)' || v === 'initial' || v === 'none';
    }

    function processComputedStylesBatch(elements, start=0){
        const batch = 250;
        const end = Math.min(elements.length, start + batch);
        for(let i=start;i<end;i++){
            const el = elements[i];
            try{
                const cs = getComputedStyle(el);
                computedCheckProps.forEach(prop=>{
                    try{
                        const val = cs.getPropertyValue(prop);
                        if(!val || isTransparentValue(val)) return;
                        const parsed = parseColorString(val);
                        if(!parsed) return;
                        const conv = convertBasedOnDetection(parsed);
                        if(conv && (conv.r !== parsed.r || conv.g !== parsed.g || conv.b !== parsed.b || conv.a !== parsed.a)){
                            el.style.setProperty(prop, rgbaToCss(conv), 'important');
                        }
                    }catch(e){}
                });

                try{
                    const bs = cs.getPropertyValue('box-shadow');
                    if(bs && bs !== 'none' && /#|rgba?\(|hsla?\(/i.test(bs)){
                        const newBs = replaceColorTokensInText(bs);
                        if(newBs !== bs) el.style.setProperty('box-shadow', newBs, 'important');
                    }
                }catch(e){}

                try{
                    const bg = cs.getPropertyValue('background-image');
                    if(bg && bg !== 'none' && /#|rgba?\(|hsla?\(/i.test(bg)){
                        const newBg = replaceColorTokensInText(bg);
                        if(newBg !== bg) el.style.setProperty('background-image', newBg, 'important');
                    }
                }catch(e){}

            }catch(e){}
        }

        if(end < elements.length){
            setTimeout(()=> processComputedStylesBatch(elements, end), 20);
        }
    }

    function processAllComputedStylesRoot(root=document){
        const elements = Array.from(root.querySelectorAll('*'));
        processComputedStylesBatch(elements, 0);
    }

    /* ---------- canvas context patch ---------- */

    function patchCanvasContext(ctx){
        try{
            if(!ctx) return;
            if(ctx.__colorPatched) return;
            ctx.__colorPatched = true;

            const convertMaybe = (v) => {
                if(typeof v !== 'string') return v;
                const parsed = parseColorString(v);
                if(!parsed) return v;
                const conv = convertBasedOnDetection(parsed);
                if(conv && (conv.r !== parsed.r || conv.g !== parsed.g || conv.b !== parsed.b || conv.a !== parsed.a)){
                    return rgbaToCss(conv);
                }
                return v;
            };

            let _fill = ctx.fillStyle;
            let _stroke = ctx.strokeStyle;
            Object.defineProperty(ctx, 'fillStyle', {
                configurable: true,
                enumerable: true,
                get(){ return _fill; },
                set(v){ _fill = convertMaybe(v); }
            });
            Object.defineProperty(ctx, 'strokeStyle', {
                configurable: true,
                enumerable: true,
                get(){ return _stroke; },
                set(v){ _stroke = convertMaybe(v); }
            });

            let _shadow = ctx.shadowColor;
            Object.defineProperty(ctx, 'shadowColor', {
                configurable: true,
                enumerable: true,
                get(){ return _shadow; },
                set(v){ _shadow = convertMaybe(v); }
            });

        }catch(e){}
    }

    // Canvas context override (chart canvas hariç)
    const origGetCtx = HTMLCanvasElement.prototype.getContext;
    HTMLCanvasElement.prototype.getContext = function(type, ...args){
        const ctx = origGetCtx.apply(this, [type, ...args]);
        try{
            if(type === '2d' && ctx){
                try{
                    if(this.closest && this.closest('.tv-lightweight-charts')) {
                        return ctx;
                    }
                }catch(e){}
                patchCanvasContext(ctx);
            }
        }catch(e){}
        return ctx;
    };

    /* ---------- GELİŞTİRİLMİŞ GRAFİK RENK KONTROLÜ ---------- */

    function applyChartColorFilters() {
        try {
            const chartStyle = document.createElement('style');
            chartStyle.textContent = `
                /* TradingView chart renk filtreleri */
                .tv-lightweight-charts {
                    filter: none !important;
                }

                /* Yeşil mumları maviye çevir */
                .tv-lightweight-charts [fill="#25a750"],
                .tv-lightweight-charts [stroke="#25a750"],
                .tv-lightweight-charts [fill*="25a750"],
                .tv-lightweight-charts [stroke*="25a750"] {
                    fill: ${rgbaToCss(UP_COLOR)} !important;
                    stroke: ${rgbaToCss(UP_COLOR)} !important;
                }

                /* Kırmızı mumları mora çevir */
                .tv-lightweight-charts [fill="#ca3f64"],
                .tv-lightweight-charts [stroke="#ca3f64"],
                .tv-lightweight-charts [fill*="ca3f64"],
                .tv-lightweight-charts [stroke*="ca3f64"] {
                    fill: ${rgbaToCss(DOWN_COLOR)} !important;
                    stroke: ${rgbaToCss(DOWN_COLOR)} !important;
                }

                /* Canvas tabanlı grafikler için CSS filtre*/
                .tv-lightweight-charts canvas {
                    filter: hue-rotate(0deg) saturate(1.2) !important;
                }

                /* Arka plan renkleri */
                .tv-lightweight-charts [fill*="rgba(37, 167, 80"],
                .tv-lightweight-charts [style*="25a750"] {
                    fill: ${rgbaToCss({...UP_COLOR, a: 0.3})} !important;
                }

                .tv-lightweight-charts [fill*="rgba(202, 63, 100"],
                .tv-lightweight-charts [style*="ca3f64"] {
                    fill: ${rgbaToCss({...DOWN_COLOR, a: 0.3})} !important;
                }

                /* Boost ikonları */
                .nav-item.nav-boost-item.nav-right-item > a.nav-item.nav-boost.icon {
                    background-color: ${rgbaToCss({...UP_COLOR,a:0.15})} !important;
                }

                .nav-item.nav-boost-item.nav-right-item img.nav-boost-icon {
                    filter: hue-rotate(210deg) saturate(1.6) brightness(1.05) !important;
                }
            `;
            document.head.appendChild(chartStyle);
        } catch (e) {
            console.error('Chart style hatası:', e);
        }
    }

    /* ---------- GRAFİK CANVAS RENK DEĞİŞİMİ ---------- */

    function monitorChartColors() {
        // Canvas fill/stroke override for chart colors
        const origFill = CanvasRenderingContext2D.prototype.fill;
        const origStroke = CanvasRenderingContext2D.prototype.stroke;
        const origFillRect = CanvasRenderingContext2D.prototype.fillRect;

        CanvasRenderingContext2D.prototype.fill = function() {
            if (this.fillStyle && typeof this.fillStyle === 'string') {
                const parsed = parseColorString(this.fillStyle);
                if (parsed) {
                    const converted = convertBasedOnDetection(parsed);
                    if (converted && (converted.r !== parsed.r || converted.g !== parsed.g || converted.b !== parsed.b)) {
                        this.fillStyle = rgbaToCss(converted);
                    }
                }
            }
            origFill.apply(this, arguments);
        };

        CanvasRenderingContext2D.prototype.stroke = function() {
            if (this.strokeStyle && typeof this.strokeStyle === 'string') {
                const parsed = parseColorString(this.strokeStyle);
                if (parsed) {
                    const converted = convertBasedOnDetection(parsed);
                    if (converted && (converted.r !== parsed.r || converted.g !== parsed.g || converted.b !== parsed.b)) {
                        this.strokeStyle = rgbaToCss(converted);
                    }
                }
            }
            origStroke.apply(this, arguments);
        };

        CanvasRenderingContext2D.prototype.fillRect = function(x, y, w, h) {
            if (this.fillStyle && typeof this.fillStyle === 'string') {
                const parsed = parseColorString(this.fillStyle);
                if (parsed) {
                    const converted = convertBasedOnDetection(parsed);
                    if (converted && (converted.r !== parsed.r || converted.g !== parsed.g || converted.b !== parsed.b)) {
                        this.fillStyle = rgbaToCss(converted);
                    }
                }
            }
            origFillRect.apply(this, arguments);
        };
    }

    /* ---------- mutation observer & iş akışı ---------- */

    function processNodeAndChildren(node){
        try{
            if(node.nodeType !== 1) return;
            processInlineAndSvg(node);
            processAllComputedStylesRoot(node);
        }catch(e){}
    }

    const mo = new MutationObserver((mutations)=>{
        const added = [];
        for(const m of mutations){
            if(m.type === 'attributes'){
                const target = m.target;
                try{
                    if(m.attributeName === 'style' || m.attributeName === 'class' || m.attributeName === 'fill' || m.attributeName === 'stroke'){
                        processNodeAndChildren(target);
                    }
                }catch(e){}
            }
            if(m.addedNodes && m.addedNodes.length){
                m.addedNodes.forEach(n => {
                    if(n.nodeType === 1) added.push(n);
                });
            }
        }
        if(added.length){
            setTimeout(()=> added.forEach(n => processNodeAndChildren(n)), 10);
        }
    });

    function kickAll(){
        try{
            processStyleSheets();
            processStyleTags();
            processInlineAndSvg(document);
            processAllComputedStylesRoot(document);
            applyChartColorFilters();
        }catch(e){}
    }

    function init() {
        kickAll();
        mo.observe(document.body || document.documentElement, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['style', 'class', 'fill', 'stroke']
        });

        monitorChartColors();

        // Sayfa yüklendikten sonra tekrar çalıştır
        window.addEventListener('load', () => {
            setTimeout(kickAll, 500);
            setTimeout(kickAll, 2000);
            setTimeout(kickAll, 5000);
        });

        // URL değişimlerini izle
        let lastUrl = location.href;
        setInterval(() => {
            if (location.href !== lastUrl) {
                lastUrl = location.href;
                setTimeout(kickAll, 1000);
            }
        }, 1000);
    }

    // Başlat
    if(document.body){
        init();
    } else {
        window.addEventListener('DOMContentLoaded', init);
    }

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址