eBay UI Enhancer (Price Stats + Item Page Charts)

Enhance eBay with dark mode, compact view, delivery toggles, ad blocking, seller badges, keyword filters, and now local price tracking with change stats and item-page mini-charts. Built by Eliminater74.

// ==UserScript==
// @name         eBay UI Enhancer (Price Stats + Item Page Charts)
// @namespace    https://greasyfork.org/users/Eliminater74
// @version      3.5
// @description  Enhance eBay with dark mode, compact view, delivery toggles, ad blocking, seller badges, keyword filters, and now local price tracking with change stats and item-page mini-charts. Built by Eliminater74.
// @author       Eliminater74
// @license      MIT
// @match        https://www.ebay.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const SETTINGS_KEY = 'ebayEnhancerSettings';
    const PRICE_CACHE_KEY = 'ebayPriceCache';

    const defaultSettings = {
        darkMode: false,
        compactView: false,
        showDelivery: true,
        showUnitPrice: true,
        hideAds: true,
        highlightTopSellers: true,
        keywordFilter: true,
        showPriceStats: true
    };

    let settings = JSON.parse(localStorage.getItem(SETTINGS_KEY)) || defaultSettings;
    let priceCache = JSON.parse(localStorage.getItem(PRICE_CACHE_KEY) || '{}');

    const gear = document.createElement('div');
    gear.innerHTML = '⚙️';
    gear.id = 'ebay-enhancer-gear';
    gear.title = 'eBay Enhancer Settings';
    gear.style.left = '20px';
    gear.style.top = '20px';
    document.body.appendChild(gear);

    const panel = document.createElement('div');
    panel.id = 'ebay-enhancer-panel';
    panel.innerHTML = `
        <label><input type="checkbox" id="darkModeToggle"> Dark Mode</label><br>
        <label><input type="checkbox" id="compactToggle"> Compact View</label><br>
        <label><input type="checkbox" id="deliveryToggle"> Show Delivery Info</label><br>
        <label><input type="checkbox" id="unitPriceToggle"> Show Unit Price</label><br>
        <label><input type="checkbox" id="hideAdsToggle"> Hide Sponsored Items</label><br>
        <label><input type="checkbox" id="highlightSellersToggle"> Highlight Top Rated Sellers</label><br>
        <label><input type="checkbox" id="keywordFilterToggle"> Filter by Keywords</label><br>
        <label><input type="checkbox" id="priceStatsToggle"> Show Price Change Stats</label>
    `;
    document.body.appendChild(panel);

    panel.querySelectorAll('input[type="checkbox"]').forEach(input => {
        const id = input.id;
        const key = id.replace('Toggle', '');
        input.checked = settings[key];
        input.addEventListener('change', () => {
            settings[key] = input.checked;
            localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
            applyEnhancements();
        });
    });

    gear.addEventListener('click', () => {
        panel.style.display = panel.style.display === 'block' ? 'none' : 'block';
    });

    function applyEnhancements() {
        document.body.classList.toggle('ebay-dark-mode', settings.darkMode);
        document.body.classList.toggle('ebay-compact-mode', settings.compactView);

        if (settings.hideAds) {
            document.querySelectorAll('[data-testid="spnAd"], [aria-label="Sponsored"]').forEach(ad => ad.remove());
        }

        if (settings.highlightTopSellers) {
            document.querySelectorAll('.s-item__etrs-text, .s-item__etrs-badge').forEach(el => {
                el.style.backgroundColor = '#d4f7d4';
                el.style.borderRadius = '4px';
                el.style.padding = '2px 4px';
            });
        }

        if (settings.keywordFilter) {
            const blocked = ['broken', 'damaged', 'for parts'];
            document.querySelectorAll('.s-item').forEach(item => {
                const txt = item.innerText.toLowerCase();
                if (blocked.some(term => txt.includes(term))) {
                    item.style.display = 'none';
                }
            });
        }

        if (settings.showPriceStats) {
            document.querySelectorAll('.s-item').forEach(item => {
                const title = item.querySelector('.s-item__title')?.innerText.trim();
                const priceEl = item.querySelector('.s-item__price');
                if (!title || !priceEl) return;

                const priceText = priceEl.innerText.replace(/[^\d.]/g, '').trim();
                const currentPrice = parseFloat(priceText);
                if (isNaN(currentPrice)) return;

                const key = title.toLowerCase();
                const history = priceCache[key] || [];
                const prev = history.at(-1);

                let change = '';
                if (prev && prev !== currentPrice) {
                    const diff = currentPrice - prev;
                    const pct = ((diff / prev) * 100).toFixed(1);
                    change = `<span style="color:${diff < 0 ? 'green' : 'red'}; font-size: 12px;">(${diff < 0 ? '▼' : '▲'} $${Math.abs(diff).toFixed(2)} / ${Math.abs(pct)}%)</span>`;
                }

                priceEl.innerHTML += ` <span class="price-change-tag">${change}</span>`;
                if (!Array.isArray(history)) priceCache[key] = [];
                if (priceCache[key].at(-1) !== currentPrice) {
                    priceCache[key].push(currentPrice);
                    if (priceCache[key].length > 10) priceCache[key].shift();
                }
            });
            localStorage.setItem(PRICE_CACHE_KEY, JSON.stringify(priceCache));
        }

        // Handle product page separately
        if (document.querySelector('#itemTitle')) {
            const itemTitle = document.querySelector('#itemTitle')?.innerText.replace(/^Details about\s*/, '').trim();
            const priceEl = document.querySelector('#prcIsum, .x-price-approx__price');
            if (itemTitle && priceEl) {
                const priceText = priceEl.innerText.replace(/[^\d.]/g, '').trim();
                const currentPrice = parseFloat(priceText);
                const key = itemTitle.toLowerCase();
                if (!isNaN(currentPrice)) {
                    const history = priceCache[key] || [];
                    const last = history.at(-1);
                    let chart = '';
                    if (history.length > 1) {
                        const min = Math.min(...history);
                        const max = Math.max(...history);
                        const scale = n => Math.round(((n - min) / (max - min)) * 7);
                        const bars = ['▁','▂','▃','▄','▅','▆','▇','█'];
                        chart = history.map(p => bars[scale(p)]).join('');
                    }
                    const statsBox = document.createElement('div');
                    statsBox.innerHTML = `<div style="margin-top:6px;font-size:13px;">Price History: ${chart || '—'}<br><strong>Last Seen:</strong> $${last ?? '—'}<br><strong>Now:</strong> $${currentPrice}</div>`;
                    priceEl.parentElement.appendChild(statsBox);
                    if (!Array.isArray(history)) priceCache[key] = [];
                    if (priceCache[key].at(-1) !== currentPrice) {
                        priceCache[key].push(currentPrice);
                        if (priceCache[key].length > 10) priceCache[key].shift();
                    }
                    localStorage.setItem(PRICE_CACHE_KEY, JSON.stringify(priceCache));
                }
            }
        }
    }

    applyEnhancements();

    const style = document.createElement('style');
    style.textContent = `
        #ebay-enhancer-gear {
            position: fixed;
            font-size: 24px;
            background: #333;
            color: white;
            padding: 6px 10px;
            border-radius: 50%;
            cursor: pointer;
            z-index: 99999;
        }
        #ebay-enhancer-panel {
            position: fixed;
            left: 20px;
            top: 60px;
            background: #fff;
            color: #000;
            padding: 10px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.3);
            display: none;
            z-index: 99999;
        }
        body.ebay-dark-mode {
            background-color: #1e1e1e !important;
            color: #ddd !important;
        }
        body.ebay-dark-mode a { color: #4ea3ff !important; }
        body.ebay-compact-mode .s-item {
            padding: 6px !important;
            font-size: 14px !important;
        }
        .price-change-tag {
            margin-left: 4px;
            font-weight: bold;
        }
    `;
    document.head.appendChild(style);
})();