Torn Bazaar Quick Pricer

Auto-fill bazaar items with market-based pricing (PDA optimized)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Torn Bazaar Quick Pricer
// @namespace    http://tampermonkey.net/
// @version      2.8.3
// @description  Auto-fill bazaar items with market-based pricing (PDA optimized)
// @author       Zedtrooper [3028329]
// @license      MIT
// @match        https://www.torn.com/bazaar.php*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @connect      api.torn.com
// @run-at       document-end
// @homepage     https://github.com/Musa-dabwe/Torn-Bazaar-Quick-Pricer
// @supportURL   https://github.com/Musa-dabwe/Torn-Bazaar-Quick-Pricer/issues
// ==/UserScript==

(function() {
    'use strict';

    console.log('[BazaarQuickPricer] v2.8.3 Starting (PDA optimized)...');

    // Configuration
    const CONFIG = {
        defaultDiscount: GM_getValue('discountPercent', 0),
        apiKey: GM_getValue('tornApiKey', ''),
        lastPriceUpdate: GM_getValue('lastPriceUpdate', 0),
        priceCache: GM_getValue('priceCache', {}),
        disableNpcCheck: GM_getValue('disableNpcCheck', false),
        cacheTimeout: 5 * 60 * 1000
    };

    const processedItems = new WeakSet();
    const processedManageItems = new WeakSet();
    let mutationDebounceTimer = null;
    const isMobile = window.innerWidth <= 784;
    let buttonsAdded = false;
    let manageButtonsAdded = false;

    // Detect dark mode
    function isDarkMode() {
        return document.body.classList.contains('dark-mode');
    }

    // Get appropriate text color based on theme
    function getTextColor() {
        return isDarkMode() ? '#767676' : '#7F7F7F';
    }

    function saveConfig() {
        GM_setValue('discountPercent', CONFIG.defaultDiscount);
        GM_setValue('tornApiKey', CONFIG.apiKey);
        GM_setValue('lastPriceUpdate', CONFIG.lastPriceUpdate);
        GM_setValue('priceCache', CONFIG.priceCache);
        GM_setValue('disableNpcCheck', CONFIG.disableNpcCheck);
    }

    // Custom SVGs
    const addButtonSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M3,7.5v11c0,1.38,1.12,2.5,2.5,2.5h1c.83,0,1.5,.67,1.5,1.5s-.67,1.5-1.5,1.5h-1c-3.03,0-5.5-2.47-5.5-5.5V7.5C0,4.47,2.47,2,5.5,2h.35c.56-1.18,1.76-2,3.15-2h2c1.39,0,2.59,.82,3.15,2h.35c1.96,0,3.78,1.05,4.76,2.75,.42,.72,.17,1.63-.55,2.05-.24,.14-.49,.2-.75,.2-.52,0-1.02-.27-1.3-.75-.45-.77-1.28-1.25-2.17-1.25h-.35c-.56,1.18-1.76,2-3.15,2h-2c-1.39,0-2.59-.82-3.15-2h-.35c-1.38,0-2.5,1.12-2.5,2.5Zm14.5,6.5h-1c-.83,0-1.5,.67-1.5,1.5s.67,1.5,1.5,1.5h1c.83,0,1.5-.67,1.5-1.5s-.67-1.5-1.5-1.5Zm6.5-.5v6c0,2.48-2.02,4.5-4.5,4.5h-5c-2.48,0-4.5-2.02-4.5-4.5v-6c0-2.48,2.02-4.5,4.5-4.5h5c2.48,0,4.5,2.02,4.5,4.5Zm-3,0c0-.83-.67-1.5-1.5-1.5h-5c-.83,0-1.5,.67-1.5,1.5v6c0,.83,.67,1.5,1.5,1.5h5c.83,0,1.5-.67,1.5-1.5v-6Z"/></svg>`;
    const refreshSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10,10-4.48,10-10S17.52,2,12,2Zm0,18c-4.41,0-8-3.59-8-8s3.59-8,8-8,8,3.59,8,8-3.59,8-8,8Zm-1-13h2v6h-2v-6Zm0,8h2v2h-2v-2Z"/><path d="M13,7v6h4l-5,5-5-5h4V7h2Z" transform="translate(0,-1)"/></svg>`;
    const infoSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12,2A10,10,0,1,0,22,12,10,10,0,0,0,12,2Zm1,15a1,1,0,0,1-2,0V11a1,1,0,0,1,2,0ZM12,8a1.5,1.5,0,1,1,1.5-1.5A1.5,1.5,0,0,1,12,8Z"/></svg>`;

    function showApiKeyPrompt() {
        const overlay = document.createElement('div');
        overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);z-index:99999;display:flex;align-items:center;justify-content:center;padding:20px;box-sizing:border-box;';
        overlay.innerHTML = `
            <div style="background:#2a2a2a;padding:25px;border-radius:8px;max-width:400px;width:100%;color:#fff;">
                <h2 style="margin:0 0 15px 0;color:#fff;font-size:18px;">Quick Pricer Setup</h2>
                <p style="margin:0 0 15px 0;line-height:1.5;font-size:14px;">Enter your <strong>Public API Key</strong>:</p>
                <input type="text" id="apiKeyInput" placeholder="API Key" style="width:100%;padding:10px;margin:10px 0;border:1px solid #555;border-radius:5px;box-sizing:border-box;background:#1a1a1a;color:#fff;font-size:14px;">
                <div style="display:flex;gap:10px;margin-top:15px;">
                    <button id="saveApiKey" style="flex:1;padding:10px;background:#4CAF50;color:white;border:none;border-radius:5px;cursor:pointer;font-size:14px;">Save</button>
                    <button id="cancelApiKey" style="flex:1;padding:10px;background:#f44336;color:white;border:none;border-radius:5px;cursor:pointer;font-size:14px;">Cancel</button>
                </div>
            </div>
        `;
        document.body.appendChild(overlay);

        document.getElementById('saveApiKey').onclick = () => {
            const key = document.getElementById('apiKeyInput').value.trim();
            if (key && key.length === 16) {
                CONFIG.apiKey = key;
                saveConfig();
                overlay.remove();
                location.reload();
            } else {
                alert('Please enter a valid 16-character API key');
            }
        };
        document.getElementById('cancelApiKey').onclick = () => overlay.remove();
    }

    function showSettingsPanel() {
        const overlay = document.createElement('div');
        overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);z-index:99999;display:flex;align-items:center;justify-content:center;padding:20px;box-sizing:border-box;';
        overlay.innerHTML = `
            <div style="background:#2a2a2a;padding:25px;border-radius:8px;max-width:400px;width:100%;color:#fff;">
                <h2 style="margin:0 0 15px 0;font-size:18px;">Quick Pricer Settings</h2>
                
                <div style="margin:15px 0;">
                    <label style="display:block;margin-bottom:5px;font-weight:bold;font-size:14px;">Discount %:</label>
                    <input type="number" id="discountInput" value="${CONFIG.defaultDiscount}" min="-50" max="50" step="0.5" style="width:100%;padding:10px;border:1px solid #555;border-radius:5px;background:#1a1a1a;color:#fff;font-size:14px;">
                    <small style="color:#999;font-size:11px;display:block;margin-top:5px;">Use negative values to price above market (e.g., -5 for +5%)</small>
                </div>

                <div style="margin:15px 0;">
                    <label style="display:block;margin-bottom:5px;font-weight:bold;font-size:14px;">API Key:</label>
                    <input type="text" id="apiKeyUpdateInput" value="${CONFIG.apiKey}" style="width:100%;padding:10px;border:1px solid #555;border-radius:5px;background:#1a1a1a;color:#fff;font-size:14px;">
                </div>

                <div style="margin:15px 0; display:flex; align-items:center; padding: 10px; background: #222; border-radius: 5px;">
                    <input type="checkbox" id="npcOverrideCheck" ${CONFIG.disableNpcCheck ? 'checked' : ''} style="width:18px; height:18px; margin-right:10px; cursor:pointer;">
                    <label for="npcOverrideCheck" style="font-size:13px; color:#fff; cursor:pointer; flex:1;">Disable NPC Safety Limit</label>
                    <div id="npcInfoIcon" style="color:#2196F3; cursor:pointer; display:flex; align-items:center; padding: 5px;">
                        ${infoSVG}
                    </div>
                </div>

                <button id="clearCache" style="width:100%;padding:10px;background:#ff9800;color:white;border:none;border-radius:5px;cursor:pointer;font-size:14px;margin:10px 0;">Clear Cache</button>
                <div style="display:flex;gap:10px;margin-top:15px;">
                    <button id="saveSettings" style="flex:1;padding:10px;background:#4CAF50;color:white;border:none;border-radius:5px;cursor:pointer;font-size:14px;">Save</button>
                    <button id="cancelSettings" style="flex:1;padding:10px;background:#999;color:white;border:none;border-radius:5px;cursor:pointer;font-size:14px;">Cancel</button>
                </div>
                <div style="margin-top:15px;padding-top:15px;border-top:1px solid #555;text-align:center;">
                    <small style="color:#999;font-size:12px;">
                        v2.8.3 | <a href="https://github.com/Musa-dabwe/Torn-Bazaar-Quick-Pricer" target="_blank" style="color:#2196F3;">GitHub</a>
                    </small>
                </div>
            </div>
        `;
        document.body.appendChild(overlay);

        document.getElementById('npcInfoIcon').onclick = (e) => {
            e.preventDefault();
            e.stopPropagation();
            alert("NPC Safety Limit Explanation:\n\nChecked: Items can be priced BELOW their NPC sell value based on your discount.\n\nUnchecked (Default): The script safeguards you by ensuring you never list an item for less than you could sell it to a game shop.");
        };

        document.getElementById('clearCache').onclick = () => {
            CONFIG.priceCache = {};
            CONFIG.lastPriceUpdate = 0;
            saveConfig();
            alert('Cache cleared!');
        };
        document.getElementById('saveSettings').onclick = () => {
            CONFIG.defaultDiscount = parseFloat(document.getElementById('discountInput').value);
            CONFIG.apiKey = document.getElementById('apiKeyUpdateInput').value.trim();
            CONFIG.disableNpcCheck = document.getElementById('npcOverrideCheck').checked;
            saveConfig();
            overlay.remove();
            alert('Settings saved!');
        };
        document.getElementById('cancelSettings').onclick = () => overlay.remove();
    }

    const itemIdCache = new Map();
    function getItemIdFromImage(image) {
        const src = image.src;
        if (itemIdCache.has(src)) return itemIdCache.get(src);
        const match = src.match(/\/(\d+)\//);
        if (match) {
            const itemId = parseInt(match[1], 10);
            itemIdCache.set(src, itemId);
            return itemId;
        }
        return null;
    }

    function getQuantity(itemElement) {
        const titleWrap = itemElement.querySelector('div.title-wrap');
        if (!titleWrap) return 1;
        const match = titleWrap.textContent.match(/x(\d+)/i);
        return match ? parseInt(match[1], 10) : 1;
    }

    const requestQueue = [];
    let isProcessingQueue = false;

    function processRequestQueue() {
        if (isProcessingQueue || requestQueue.length === 0) return;
        isProcessingQueue = true;
        const { itemId, callback } = requestQueue.shift();

        GM_xmlhttpRequest({
            method: 'GET',
            url: `https://api.torn.com/torn/${itemId}?selections=items&key=${CONFIG.apiKey}`,
            onload: function(response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data.error) {
                        if (data.error.code === 2) {
                            alert('Incorrect API Key!');
                            CONFIG.apiKey = null;
                            saveConfig();
                        }
                        callback({ marketValue: 0, sellPrice: 0 });
                    } else if (data.items?.[itemId]) {
                        const itemData = data.items[itemId];
                        const marketValue = itemData.market_value || 0;
                        const sellPrice = itemData.sell_price || 0;

                        CONFIG.priceCache[itemId] = {
                            marketValue: marketValue,
                            sellPrice: sellPrice,
                            timestamp: Date.now()
                        };
                        CONFIG.lastPriceUpdate = Date.now();
                        saveConfig();

                        callback({ marketValue, sellPrice });
                    } else {
                        callback({ marketValue: 0, sellPrice: 0 });
                    }
                } catch (e) {
                    console.error('[BazaarQuickPricer] Parse error:', e);
                    callback({ marketValue: 0, sellPrice: 0 });
                }
                isProcessingQueue = false;
                setTimeout(processRequestQueue, 300);
            },
            onerror: function() {
                callback({ marketValue: 0, sellPrice: 0 });
                isProcessingQueue = false;
                setTimeout(processRequestQueue, 300);
            }
        });
    }

    function fetchItemData(itemId, callback) {
        const now = Date.now();
        const cached = CONFIG.priceCache[itemId];

        if (cached && cached.timestamp && (now - cached.timestamp < CONFIG.cacheTimeout)) {
            callback({
                marketValue: cached.marketValue,
                sellPrice: cached.sellPrice
            });
            return;
        }

        requestQueue.push({ itemId, callback });
        processRequestQueue();
    }

    function calculateFinalPrice(marketValue, sellPrice, discount) {
        let finalPrice = Math.round(marketValue * (1 - discount / 100));
        
        // Safety Check
        if (!CONFIG.disableNpcCheck && sellPrice > 0 && finalPrice < sellPrice) {
            console.log(`[BazaarQuickPricer] Price ${finalPrice} below NPC sell price ${sellPrice}, adjusting...`);
            finalPrice = sellPrice;
        }

        return finalPrice;
    }

    // Helper to clear inputs
    function clearItemInputs(itemElement) {
        const amountDiv = itemElement.querySelector('div.amount-main-wrap');
        if (!amountDiv) return;

        // 1. Clear Price Inputs
        const priceInputs = amountDiv.querySelectorAll('div.price div input');
        priceInputs.forEach(input => {
            input.value = '';
            input.dispatchEvent(new Event('input', { bubbles: true }));
        });

        // 2. Clear Quantity or Uncheck
        const isQuantityCheckbox = amountDiv.querySelector('div.amount.choice-container');
        if (isQuantityCheckbox) {
            // If it's a checkbox (common for single items), uncheck it
            const checkbox = isQuantityCheckbox.querySelector('input');
            if (checkbox && checkbox.checked) checkbox.click();
        } else {
            // If it's a text input (bulk items), clear the text
            const quantityInput = amountDiv.querySelector('div.amount input');
            if (quantityInput) {
                quantityInput.value = ''; 
                quantityInput.dispatchEvent(new Event('input', { bubbles: true }));
            }
        }
    }

    function fillItemPrice(itemElement) {
        const image = itemElement.querySelector('div.image-wrap img');
        if (!image) return Promise.resolve();

        const itemId = getItemIdFromImage(image);
        if (!itemId) return Promise.resolve();

        const amountDiv = itemElement.querySelector('div.amount-main-wrap');
        if (!amountDiv) return Promise.resolve();
        const priceInputs = amountDiv.querySelectorAll('div.price div input');
        if (priceInputs.length === 0) return Promise.resolve();

        return new Promise((resolve) => {
            fetchItemData(itemId, ({ marketValue, sellPrice }) => {
                if (marketValue > 0) {
                    const finalPrice = calculateFinalPrice(marketValue, sellPrice, CONFIG.defaultDiscount);

                    priceInputs[0].value = finalPrice;
                    priceInputs[1].value = finalPrice;
                    priceInputs[0].dispatchEvent(new Event('input', { bubbles: true }));

                    const isQuantityCheckbox = amountDiv.querySelector('div.amount.choice-container');
                    if (isQuantityCheckbox) {
                        const checkbox = isQuantityCheckbox.querySelector('input');
                        if (checkbox && !checkbox.checked) checkbox.click();
                    } else {
                        const quantityInput = amountDiv.querySelector('div.amount input');
                        if (quantityInput) {
                            quantityInput.value = getQuantity(itemElement);
                            quantityInput.dispatchEvent(new Event('input', { bubbles: true }));
                            quantityInput.dispatchEvent(new Event('keyup', { bubbles: true }));
                        }
                    }

                    // UPDATE BUTTON VISUALS FOR UNDO
                    const btn = itemElement.querySelector('.quick-price-btn button');
                    if (btn) {
                        btn.style.background = '#E3392C'; // Red
                        btn.dataset.mode = 'undo';
                    }
                }
                resolve();
            });
        });
    }

    function getActiveTab() {
        const tabs = document.querySelectorAll('ul.items-tabs li');
        for (const tab of tabs) {
            if (tab.classList.contains('active')) {
                return tab.getAttribute('data-category') || 'all';
            }
        }
        return 'all';
    }

    function getVisibleItems() {
        const activeTab = getActiveTab();
        const allItemsLists = document.querySelectorAll('ul.items-cont');

        for (const list of allItemsLists) {
            const style = window.getComputedStyle(list);
            if (style.display !== 'none') {
                const items = list.querySelectorAll('li.clearfix:not(.disabled)');
                return Array.from(items);
            }
        }

        return [];
    }

    // ===== MANAGE ITEMS PAGE FUNCTIONS =====

    function updateManageItemPrice(priceDiv, itemId) {
        const priceInput = priceDiv.querySelector('input.input-money');
        if (!priceInput) return;

        const currentPrice = parseInt(priceInput.value.replace(/,/g, '')) || 0;

        priceInput.disabled = true;
        priceInput.style.opacity = '0.5';
        fetchItemData(itemId, ({ marketValue, sellPrice }) => {
            priceInput.disabled = false;
            priceInput.style.opacity = '1';

            if (marketValue > 0) {
                const newPrice = calculateFinalPrice(marketValue, sellPrice, CONFIG.defaultDiscount);

                // Warn if price change is significant (>20% difference)
                const priceDiff = Math.abs(newPrice - currentPrice);
                const percentDiff = currentPrice > 0 ? (priceDiff / currentPrice) * 100 : 100;

                if (percentDiff > 20 && currentPrice > 0) {
                    const direction = newPrice > currentPrice ? 'increase' : 'decrease';
                    const confirmed = confirm(
                        `Price ${direction} detected!\n\n` +
                        `Current: $${currentPrice.toLocaleString()}\n` +
                        `New: $${newPrice.toLocaleString()}\n` +
                        `Difference: ${percentDiff.toFixed(1)}%\n\n` +
                        `Update to new price?`
                    );
                    if (!confirmed) return;
                }

                // Update the price
                priceInput.value = newPrice;
                priceInput.dispatchEvent(new Event('input', { bubbles: true }));
                priceInput.dispatchEvent(new Event('change', { bubbles: true }));
                // Visual feedback
                const borderColor = (sellPrice > 0 && newPrice === sellPrice) ? '#ff9800' : '#5F5F5F';
                priceInput.style.border = `2px solid ${borderColor}`;
                setTimeout(() => priceInput.style.border = '', 1000);
            } else {
                alert('Could not fetch price for this item');
            }
        });
    }

    function addUpdatePriceButton(manageItem) {
        if (processedManageItems.has(manageItem)) return;
        const priceDiv = manageItem.querySelector('div[class*="price"]');
        if (!priceDiv) return;

        // Check if button already exists
        if (priceDiv.querySelector('.quick-update-price-btn')) {
            processedManageItems.add(manageItem);
            return;
        }

        processedManageItems.add(manageItem);
        // Find item image to get item ID
        const image = manageItem.querySelector('img');
        if (!image) return;

        const itemId = getItemIdFromImage(image);
        if (!itemId) return;
        // Create update button
        const btnContainer = document.createElement('div');
        btnContainer.className = 'quick-update-price-btn';
        btnContainer.style.cssText = 'display:inline-block;margin-left:10px;vertical-align:middle;';

        const btnInput = document.createElement('button');
        btnInput.innerHTML = refreshSVG;
        btnInput.style.cssText = 'background:#5F5F5F;color:white;padding:6px;border:none;border-radius:4px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;box-shadow:0 2px 4px rgba(0,0,0,0.2);transition:background 0.2s;';
        btnInput.setAttribute('title', 'Update Price');
        btnInput.addEventListener('mouseenter', () => {
            btnInput.style.background = '#4F4F4F';
        });
        btnInput.addEventListener('mouseleave', () => {
            if (!btnInput.disabled) btnInput.style.background = '#5F5F5F';
        });
        btnContainer.appendChild(btnInput);

        // Insert button after the price input
        const inputGroup = priceDiv.querySelector('.input-money-group');
        if (inputGroup) {
            inputGroup.parentNode.insertBefore(btnContainer, inputGroup.nextSibling);
        }

        btnInput.addEventListener('click', function(event) {
            event.stopPropagation();
            event.preventDefault();
            updateManageItemPrice(priceDiv, itemId);
        });
    }

    function getManageItems() {
        // Look for manage items list
        const manageItemsList = document.querySelectorAll('div[class*="item___"]');
        return Array.from(manageItemsList);
    }

    async function updateAllManagePrices() {
        const items = getManageItems();
        console.log('[BazaarQuickPricer] Updating', items.length, 'manage items...');

        if (items.length === 0) {
            alert('No items found to update!');
            return;
        }

        const updateButton = document.getElementById('quickUpdateAllPricesBtn');
        if (updateButton) {
            updateButton.disabled = true;
            updateButton.style.opacity = '0.5';
            updateButton.textContent = 'Updating...';
        }

        let updated = 0;
        for (const item of items) {
            const priceDiv = item.querySelector('div[class*="price"]');
            const image = item.querySelector('img');

            if (priceDiv && image) {
                const itemId = getItemIdFromImage(image);
                if (itemId) {
                    await new Promise((resolve) => {
                        updateManageItemPrice(priceDiv, itemId);
                        setTimeout(resolve, 350);
                    });
                    updated++;
                }
            }
        }

        if (updateButton) {
            updateButton.disabled = false;
            updateButton.style.opacity = '1';
            updateButton.textContent = 'Update All';
        }

        alert(`Updated ${updated} item prices!`);
        console.log('[BazaarQuickPricer] Update complete!');
    }

    function addManagePageButtons() {
        if (manageButtonsAdded) return;
        let attempts = 0;
        const maxAttempts = 20;

        const tryAddButtons = setInterval(() => {
            attempts++;

            // Find header with broader selector support for current Torn UI
            const headings = Array.from(document.querySelectorAll('div[role="heading"], div[class*="title"], div[class*="panelHeader"]'));
            // Look for "Manage your Bazaar" or "Manage items"
            let manageHeading = headings.find(h => h.textContent.includes('Manage your Bazaar') || h.textContent.includes('Manage items'));

            if (manageHeading) {
                // Check for the SETTINGS button ID, which exists on both mobile and desktop
                if (document.getElementById('manageSettingsBtn')) {
                    clearInterval(tryAddButtons);
                    manageButtonsAdded = true;
                    return;
                }

                clearInterval(tryAddButtons);
                manageButtonsAdded = true;

                // Container for buttons floating right
                const buttonContainer = document.createElement('div');
                buttonContainer.style.cssText = 'float: right; display: flex; gap: 5px; align-items: center; margin-top: -2px;';

                // 1. Update All Button
                const updateAllBtn = document.createElement('button');
                updateAllBtn.id = 'quickUpdateAllPricesBtn';
                updateAllBtn.textContent = 'Update All';
                updateAllBtn.style.cssText = 'background:#5F5F5F;color:white;padding:6px 12px;border:none;border-radius:4px;cursor:pointer;font-size:12px;box-shadow:0 2px 4px rgba(0,0,0,0.2);';
                updateAllBtn.setAttribute('title', 'Update all item prices to current market value');
                updateAllBtn.addEventListener('click', updateAllManagePrices);

                // 2. Settings Button
                const settingsBtn = document.createElement('button');
                settingsBtn.id = 'manageSettingsBtn';
                settingsBtn.textContent = 'Settings';
                settingsBtn.style.cssText = 'background:#5F5F5F;color:white;padding:6px 12px;border:none;border-radius:4px;cursor:pointer;font-size:12px;box-shadow:0 2px 4px rgba(0,0,0,0.2);';
                settingsBtn.setAttribute('title', 'Open Quick Pricer settings');
                settingsBtn.addEventListener('click', (e) => {
                    e.preventDefault();
                    showSettingsPanel();
                });

                // Add Hover Effects
                [updateAllBtn, settingsBtn].forEach(btn => {
                    btn.addEventListener('mouseenter', () => {
                        if (!btn.disabled) btn.style.background = '#4F4F4F';
                    });
                    btn.addEventListener('mouseleave', () => {
                        if (!btn.disabled) btn.style.background = '#5F5F5F';
                    });
                });

                // Only add "Update All" button if NOT on mobile
                if (!isMobile) {
                    buttonContainer.appendChild(updateAllBtn);
                }

                buttonContainer.appendChild(settingsBtn);

                manageHeading.appendChild(buttonContainer);

                console.log('[BazaarQuickPricer] Manage page buttons added');
            } else if (attempts >= maxAttempts) {
                clearInterval(tryAddButtons);
                console.log('[BazaarQuickPricer] Manage page buttons failed to add (header not found)');
            }
        }, 500);
    }

    function processManageItems() {
        const items = getManageItems();
        console.log('[BazaarQuickPricer] Found', items.length, 'manage items');
        if (items.length > 0) {
            items.forEach(item => addUpdatePriceButton(item));
        }
    }

    // ===== ADD ITEMS PAGE FUNCTIONS =====

    function addQuickPriceButton(itemElement) {
        if (processedItems.has(itemElement)) return;
        const titleWrap = itemElement.querySelector('div.title-wrap');
        if (!titleWrap) return;

        if (titleWrap.querySelector('.quick-price-btn')) {
            processedItems.add(itemElement);
            return;
        }

        processedItems.add(itemElement);

        const image = itemElement.querySelector('div.image-wrap img');
        if (!image) return;
        const itemId = getItemIdFromImage(image);
        if (!itemId) return;

        const amountDiv = itemElement.querySelector('div.amount-main-wrap');
        if (!amountDiv) return;

        const priceInputs = amountDiv.querySelectorAll('div.price div input');
        if (priceInputs.length === 0) return;

        const btnContainer = document.createElement('div');
        btnContainer.className = 'quick-price-btn';
        btnContainer.style.cssText = 'position:absolute;right:10px;top:50%;transform:translateY(-50%);z-index:10;';

        const btnInput = document.createElement('button');
        btnInput.innerHTML = addButtonSVG;
        btnInput.style.cssText = 'background:#5F5F5F;color:white;padding:8px;border:none;border-radius:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px rgba(0,0,0,0.2);transition:background 0.2s;';
        btnInput.setAttribute('title', 'Quick Add / Undo');
        btnInput.dataset.mode = 'add'; // Default mode

        // Handle Hover based on mode
        btnInput.addEventListener('mouseenter', () => {
            if (btnInput.dataset.mode === 'undo') {
                btnInput.style.background = '#C03025'; // Darker red
            } else {
                btnInput.style.background = '#4F4F4F'; // Darker grey
            }
        });

        btnInput.addEventListener('mouseleave', () => {
            if (!btnInput.disabled) {
                if (btnInput.dataset.mode === 'undo') {
                    btnInput.style.background = '#E3392C'; // Red
                } else {
                    btnInput.style.background = '#5F5F5F'; // Grey
                }
            }
        });

        btnContainer.appendChild(btnInput);
        titleWrap.style.position = 'relative';
        titleWrap.appendChild(btnContainer);

        // Click Handler (Toggle Add/Undo)
        btnInput.addEventListener('click', function(event) {
            event.stopPropagation();
            
            if (btnInput.dataset.mode === 'undo') {
                // UNDO ACTION
                clearItemInputs(itemElement);
                // Reset styling
                btnInput.style.background = '#5F5F5F';
                btnInput.dataset.mode = 'add';
            } else {
                // ADD ACTION
                btnInput.disabled = true;
                btnInput.style.opacity = '0.5';

                fillItemPrice(itemElement).then(() => {
                    btnInput.disabled = false;
                    btnInput.style.opacity = '1';
                });
            }
        });
    }

    async function fillAllItems() {
        const items = getVisibleItems();
        console.log('[BazaarQuickPricer] Filling', items.length, 'items in current tab simultaneously...');

        if (items.length === 0) {
            alert('No items found to fill!');
            return;
        }

        const fillButton = document.getElementById('quickFillAllBtn');
        if (fillButton) {
            fillButton.disabled = true;
            fillButton.style.opacity = '0.5';
            fillButton.textContent = 'Filling...';
        }

        const promises = items.map(item => fillItemPrice(item));
        await Promise.all(promises);
        if (fillButton) {
            fillButton.disabled = false;
            fillButton.style.opacity = '1';
            fillButton.textContent = 'Quick Fill';
        }

        console.log('[BazaarQuickPricer] Fill complete!');
    }

    function addTopButtons() {
        if (buttonsAdded) return;
        let attempts = 0;
        const maxAttempts = 20;

        const tryAddButtons = setInterval(() => {
            attempts++;
            const titleSection = document.querySelector('div.title-black');

            if (titleSection && titleSection.textContent.includes('Add items to your Bazaar')) {
                if (document.getElementById('quickFillAllBtn')) {
                    clearInterval(tryAddButtons);
                    buttonsAdded = true;
                    return;
                }

                clearInterval(tryAddButtons);
                buttonsAdded = true;

                const buttonContainer = document.createElement('div');
                buttonContainer.style.cssText = 'display:inline-flex;margin-left:15px;vertical-align:top;align-items:flex-start;';

                const fillAllBtn = document.createElement('button');
                fillAllBtn.id = 'quickFillAllBtn';
                fillAllBtn.textContent = 'Quick Fill';
                fillAllBtn.style.cssText = 'background:#5F5F5F;color:white;padding:8px 14px;border:none;border-radius:4px 0 0 4px;cursor:pointer;display:inline-flex;align-items:center;font-size:13px;box-shadow:0 2px 4px rgba(0,0,0,0.2);transition:background 0.2s;border-right:1px solid #4F4F4F;';
                fillAllBtn.setAttribute('title', 'Fill all items in current tab with market prices');
                fillAllBtn.addEventListener('mouseenter', () => {
                    if (!fillAllBtn.disabled) fillAllBtn.style.background = '#4F4F4F';
                });
                fillAllBtn.addEventListener('mouseleave', () => {
                    if (!fillAllBtn.disabled) fillAllBtn.style.background = '#5F5F5F';
                });
                fillAllBtn.addEventListener('click', fillAllItems);

                const settingsBtn = document.createElement('button');
                settingsBtn.id = 'quickPricerSettingsBtn';
                settingsBtn.textContent = 'Settings';
                settingsBtn.style.cssText = 'background:#5F5F5F;color:white;padding:8px 14px;border:none;border-radius:0 4px 4px 0;cursor:pointer;display:inline-flex;align-items:center;font-size:13px;box-shadow:0 2px 4px rgba(0,0,0,0.2);transition:background 0.2s;';
                settingsBtn.setAttribute('title', 'Open Quick Pricer settings');
                settingsBtn.addEventListener('mouseenter', () => {
                    settingsBtn.style.background = '#4F4F4F';
                });
                settingsBtn.addEventListener('mouseleave', () => {
                    settingsBtn.style.background = '#5F5F5F';
                });
                settingsBtn.addEventListener('click', (e) => {
                    e.preventDefault();
                    showSettingsPanel();
                });
                buttonContainer.appendChild(fillAllBtn);
                buttonContainer.appendChild(settingsBtn);
                titleSection.appendChild(buttonContainer);

                console.log('[BazaarQuickPricer] Buttons added');
            } else if (attempts >= maxAttempts) {
                clearInterval(tryAddButtons);
                console.log('[BazaarQuickPricer] Buttons failed to add');
            }
        }, 500);
    }

    function processAllItems() {
        const items = document.querySelectorAll('ul.items-cont li.clearfix:not(.disabled)');
        console.log('[BazaarQuickPricer] Found', items.length, 'items');
        if (items.length > 0) {
            items.forEach(item => addQuickPriceButton(item));
        }
    }

    function setupObserver() {
        const bazaarRoot = document.getElementById('bazaarRoot');
        if (!bazaarRoot) {
            setTimeout(setupObserver, 1000);
            return;
        }

        console.log('[BazaarQuickPricer] Observer starting');
        const observer = new MutationObserver(() => {
            clearTimeout(mutationDebounceTimer);
            mutationDebounceTimer = setTimeout(() => {
                processAllItems();
                addTopButtons();
                processManageItems();
                addManagePageButtons();
            }, 300);
        });
        observer.observe(bazaarRoot, { childList: true, subtree: true });
    }

    function init() {
        console.log('[BazaarQuickPricer] Init starting');
        if (!CONFIG.apiKey || CONFIG.apiKey === 'null') {
            setTimeout(showApiKeyPrompt, 1000);
            return;
        }

        setTimeout(() => {
            processAllItems();
            setupObserver();
            addTopButtons();
            processManageItems();
            addManagePageButtons();
        }, 2000);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        setTimeout(init, 1000);
    }

})();