Neopets Inventory Manager

Consolidate and manage your Neopets inventory from Gallery, Shop, Inventory, and SDB with ItemDB pricing

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Neopets Inventory Manager
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  Consolidate and manage your Neopets inventory from Gallery, Shop, Inventory, and SDB with ItemDB pricing
// @author       Fatalsymptoms
// @match        https://www.neopets.com/*
// @license MIT
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @connect      itemdb.com.br
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const CONFIG = {
        ITEMDB_API: 'https://itemdb.com.br/api/v1/items/',
        STORAGE_KEY: 'neopets_inventory_data',
        PRICE_CACHE_KEY: 'neopets_price_cache',
        CACHE_DURATION: 24 * 60 * 60 * 1000, // 24 hours
        REQUEST_DELAY: 1000 // 1 second between API requests
    };

    // Data structure
    class InventoryManager {
        constructor() {
            this.inventory = GM_getValue(CONFIG.STORAGE_KEY, {
                gallery: [],
                shop: [],
                inventory: [],
                sdb: [],
                lastUpdated: {}
            });
            this.priceCache = GM_getValue(CONFIG.PRICE_CACHE_KEY, {});
        }

        save() {
            GM_setValue(CONFIG.STORAGE_KEY, this.inventory);
            GM_setValue(CONFIG.PRICE_CACHE_KEY, this.priceCache);
        }

        async fetchFromGallery() {
            console.log('Fetching from Gallery...');

            return new Promise((resolve, reject) => {
                // Check if we're already on a gallery page
                const currentUrl = window.location.href;
                const isOnGalleryPage = currentUrl.includes('/gallery/');

                if (isOnGalleryPage) {
                    // Parse current page directly
                    console.log('Already on gallery page, parsing current page...');
                    const items = this.parseGalleryPage(document);
                    this.inventory.gallery = items;
                    this.inventory.lastUpdated.gallery = new Date().toISOString();
                    this.save();
                    resolve(items);
                } else {
                    // Fetch gallery page
                    console.log('Fetching gallery page...');
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: 'https://www.neopets.com/gallery/index.phtml?view=all',
                        onload: (response) => {
                            try {
                                const parser = new DOMParser();
                                const doc = parser.parseFromString(response.responseText, 'text/html');
                                const items = this.parseGalleryPage(doc);

                                console.log(`Found ${items.length} items in gallery`);

                                this.inventory.gallery = items;
                                this.inventory.lastUpdated.gallery = new Date().toISOString();
                                this.save();

                                resolve(items);
                            } catch (error) {
                                console.error('Error parsing gallery:', error);
                                reject(error);
                            }
                        },
                        onerror: (error) => {
                            console.error('Error fetching gallery:', error);
                            reject(error);
                        }
                    });
                }
            });
        }

        parseGalleryPage(doc) {
            const items = [];

            console.log('=== PARSING GALLERY PAGE ===');

            // Find the form table
            const mainTable = doc.querySelector('form[name="gallery_form"] table[border="0"][align="center"]');

            if (!mainTable) {
                console.error('Could not find gallery form table');
                console.log('Looking for alternative gallery structure...');

                // Try the remove form structure
                const removeTable = doc.querySelector('form[id="gallery_form"] table[border="0"][align="center"]');
                if (removeTable) {
                    console.log('Found gallery_form by ID instead');
                    return this.parseGalleryTableStructure(removeTable);
                }

                return items;
            }

            return this.parseGalleryTableStructure(mainTable);
        }

        parseGalleryTableStructure(table) {
            const items = [];
            const rows = Array.from(table.querySelectorAll('tr'));

            console.log(`Found ${rows.length} rows in gallery table`);

            // Iterate through all rows looking for items
            for (let i = 0; i < rows.length; i++) {
                const row = rows[i];

                // Look for cells that contain item images
                const itemCells = row.querySelectorAll('td[width="140"][align="center"][valign="top"]');

                if (itemCells.length === 0) continue;

                console.log(`Row ${i}: Found ${itemCells.length} item cells`);

                // Process each item cell in this row
                itemCells.forEach((cell, cellIndex) => {
                    const img = cell.querySelector('img.itemimg');
                    const nameEl = cell.querySelector('b.textcolor');

                    if (!img || !nameEl) return;

                    const name = nameEl.textContent.trim();
                    const imageUrl = img.src;
                    let quantity = 1;

                    // The quantity is in the NEXT row, at the SAME cell index
                    if (i + 1 < rows.length) {
                        const qtyRow = rows[i + 1];
                        const qtyCells = qtyRow.querySelectorAll('td[align="center"]');

                        console.log(`  Looking for qty in row ${i + 1}, cell ${cellIndex}`);
                        console.log(`  Found ${qtyCells.length} cells in qty row`);

                        if (qtyCells[cellIndex]) {
                            const qtyCell = qtyCells[cellIndex];
                            const qtyHTML = qtyCell.innerHTML;
                            const qtyText = qtyCell.textContent;

                            console.log(`  Qty cell HTML: ${qtyHTML.substring(0, 100)}`);
                            console.log(`  Qty cell text: ${qtyText.trim()}`);

                            // Try to find "Qty:X" pattern
                            const qtyMatch = qtyText.match(/Qty:\s*(\d+)/i);

                            if (qtyMatch) {
                                quantity = parseInt(qtyMatch[1], 10);
                                console.log(`  ✓ Found quantity: ${quantity}`);
                            } else {
                                console.log(`  ✗ No quantity match found in: "${qtyText.trim()}"`);
                            }
                        } else {
                            console.log(`  ✗ No qty cell at index ${cellIndex}`);
                        }
                    }

                    console.log(`Adding: ${name} x${quantity}`);

                    items.push({
                        name: name,
                        quantity: quantity,
                        imageUrl: imageUrl,
                        source: 'gallery'
                    });
                });
            }

            console.log(`=== TOTAL ITEMS: ${items.length} ===`);
            return items;
        }

        async fetchFromInventory() {
            console.log('Fetching from Inventory...');
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: 'https://www.neopets.com/objects.phtml?type=inventory',
                    onload: (response) => {
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(response.responseText, 'text/html');
                        const items = this.parseInventoryPage(doc);

                        this.inventory.inventory = items;
                        this.inventory.lastUpdated.inventory = new Date().toISOString();
                        this.save();

                        resolve(items);
                    },
                    onerror: reject
                });
            });
        }

        parseInventoryPage(doc) {
            const items = [];
            const itemElements = doc.querySelectorAll('td[bgcolor]');

            itemElements.forEach(el => {
                const img = el.querySelector('img');
                const nameEl = el.querySelector('b');

                if (img && nameEl && img.src.includes('items/')) {
                    items.push({
                        name: nameEl.textContent.trim(),
                        quantity: 1,
                        imageUrl: img.src,
                        source: 'inventory'
                    });
                }
            });

            return items;
        }

        async fetchFromShop(progressCallback) {
            console.log('Fetching from Shop...');

            return new Promise(async (resolve, reject) => {
                try {
                    const allItems = [];
                    let currentPage = 0;
                    let hasMorePages = true;

                    while (hasMorePages) {
                        console.log(`Fetching shop page ${currentPage + 1}...`);

                        if (progressCallback) {
                            progressCallback(currentPage + 1, '?', `Fetching page ${currentPage + 1}...`);
                        }

                        const url = currentPage === 0
                            ? 'https://www.neopets.com/market.phtml?type=your'
                            : `https://www.neopets.com/market.phtml?type=your&lim=${currentPage * 30}`;

                        const pageItems = await this.fetchShopPage(url);

                        if (pageItems.length > 0) {
                            allItems.push(...pageItems);
                            console.log(`Page ${currentPage + 1}: Found ${pageItems.length} items (Total: ${allItems.length})`);
                            currentPage++;

                            // Add a small delay between pages
                            await new Promise(resolve => setTimeout(resolve, 500));
                        } else {
                            hasMorePages = false;
                        }

                        // Safety check - stop after 100 pages
                        if (currentPage >= 100) {
                            console.warn('Reached maximum page limit (100 pages)');
                            hasMorePages = false;
                        }
                    }

                    console.log(`Shop fetch complete: ${allItems.length} total items from ${currentPage} pages`);

                    this.inventory.shop = allItems;
                    this.inventory.lastUpdated.shop = new Date().toISOString();
                    this.save();

                    resolve(allItems);
                } catch (error) {
                    console.error('Error fetching shop:', error);
                    reject(error);
                }
            });
        }

        fetchShopPage(url) {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: url,
                    onload: (response) => {
                        try {
                            const parser = new DOMParser();
                            const doc = parser.parseFromString(response.responseText, 'text/html');
                            const items = this.parseShopPageStructure(doc);
                            resolve(items);
                        } catch (error) {
                            console.error('Error parsing shop page:', error);
                            reject(error);
                        }
                    },
                    onerror: reject
                });
            });
        }

        parseShopPageStructure(doc) {
            const items = [];

            // Find all rows in the shop table
            const rows = doc.querySelectorAll('form[action="process_market.phtml"] table tr');

            console.log(`Found ${rows.length} rows in shop table`);

            rows.forEach((row, index) => {
                // Skip header and footer rows
                const cells = row.querySelectorAll('td');
                if (cells.length < 6) return;

                // Check if this is an item row (has item name in first cell)
                const nameCell = cells[0];
                const imgCell = cells[1];
                const stockCell = cells[2];
                const priceCell = cells[4];

                const nameEl = nameCell.querySelector('b');
                const img = imgCell.querySelector('img');

                if (!nameEl || !img) return;

                const name = nameEl.textContent.trim();
                const imageUrl = img.src;

                // Extract stock quantity
                let quantity = 1;
                const stockText = stockCell.textContent.trim();
                const stockMatch = stockText.match(/\b(\d+)\b/);
                if (stockMatch) {
                    quantity = parseInt(stockMatch[1], 10);
                }

                // Extract price
                let price = 0;
                const priceInput = priceCell.querySelector('input[name^="cost_"]');
                if (priceInput) {
                    price = parseInt(priceInput.value) || 0;
                }

                console.log(`Found shop item: ${name} x${quantity} @ ${price} NP`);

                items.push({
                    name: name,
                    quantity: quantity,
                    price: price,
                    imageUrl: imageUrl,
                    source: 'shop'
                });
            });

            return items;
        }

        async fetchFromSDB(progressCallback) {
            console.log('Fetching from SDB...');

            return new Promise(async (resolve, reject) => {
                try {
                    const allItems = [];
                    let currentPage = 0;
                    let hasMorePages = true;

                    while (hasMorePages) {
                        console.log(`Fetching SDB page ${currentPage + 1}...`);

                        if (progressCallback) {
                            progressCallback(currentPage + 1, '?', `Fetching page ${currentPage + 1}...`);
                        }

                        const url = currentPage === 0
                            ? 'https://www.neopets.com/safetydeposit.phtml'
                            : `https://www.neopets.com/safetydeposit.phtml?offset=${currentPage * 30}`;

                        const pageItems = await this.fetchSDBPage(url);

                        if (pageItems.length > 0) {
                            allItems.push(...pageItems);
                            console.log(`Page ${currentPage + 1}: Found ${pageItems.length} items (Total: ${allItems.length})`);
                            currentPage++;

                            // Add a small delay between pages
                            await new Promise(resolve => setTimeout(resolve, 500));
                        } else {
                            hasMorePages = false;
                        }

                        // Safety check - stop after 200 pages (SDB can be huge!)
                        if (currentPage >= 200) {
                            console.warn('Reached maximum page limit (200 pages)');
                            hasMorePages = false;
                        }
                    }

                    console.log(`SDB fetch complete: ${allItems.length} total items from ${currentPage} pages`);

                    this.inventory.sdb = allItems;
                    this.inventory.lastUpdated.sdb = new Date().toISOString();
                    this.save();

                    resolve(allItems);
                } catch (error) {
                    console.error('Error fetching SDB:', error);
                    reject(error);
                }
            });
        }

        fetchSDBPage(url) {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: url,
                    onload: (response) => {
                        try {
                            const parser = new DOMParser();
                            const doc = parser.parseFromString(response.responseText, 'text/html');
                            const items = this.parseSDBPage(doc);
                            resolve(items);
                        } catch (error) {
                            console.error('Error parsing SDB page:', error);
                            reject(error);
                        }
                    },
                    onerror: reject
                });
            });
        }

        parseSDBPage(doc) {
            const items = [];

            console.log(`=== PARSING SDB PAGE ===`);

            // Try multiple ways to find item rows
            let rows = doc.querySelectorAll('form[action*="process_safetydeposit"] table tr[bgcolor]');

            if (rows.length === 0) {
                console.log('No rows found with form selector, trying alternative...');
                rows = doc.querySelectorAll('form[name="boxform"] table tr[bgcolor]');
            }

            if (rows.length === 0) {
                console.log('No rows found with name selector, trying ID...');
                rows = doc.querySelectorAll('form#boxform table tr[bgcolor]');
            }

            if (rows.length === 0) {
                console.log('No rows with form selectors, trying just table rows with bgcolor...');
                // Get all rows with bgcolor and filter out header/footer
                const allRows = doc.querySelectorAll('table tr[bgcolor]');
                console.log(`Found ${allRows.length} total rows with bgcolor`);

                // Filter to only rows that have item images
                rows = Array.from(allRows).filter(row => {
                    return row.querySelector('img[src*="items/"]') !== null;
                });
            }

            console.log(`Found ${rows.length} item rows in SDB page`);

            if (rows.length === 0) {
                console.error('Could not find any item rows in SDB!');
                console.log('Page structure:');
                console.log(doc.body.innerHTML.substring(0, 500));
                return items;
            }

            rows.forEach((row, rowIndex) => {
                const cells = row.querySelectorAll('td');

                if (rowIndex === 0) {
                    console.log(`First row has ${cells.length} cells - analyzing structure...`);
                    cells.forEach((cell, i) => {
                        console.log(`  Cell ${i}: ${cell.textContent.trim().substring(0, 50)}`);
                    });
                }

                if (cells.length < 5) {
                    console.log(`Row ${rowIndex}: Skipping - only ${cells.length} cells`);
                    return;
                }

                // Cell structure can vary:
                // WITHOUT price column: Image | Name | Description | Type | Qty | Remove
                // WITH price column: Image | Name | Description | Type | Price | Qty | Remove

                const imgCell = cells[0];
                const nameCell = cells[1];

                const img = imgCell.querySelector('img');
                const nameEl = nameCell.querySelector('b');

                if (!img || !nameEl) {
                    if (rowIndex < 3) {
                        console.log(`Row ${rowIndex}: Skipping - no image or name element`);
                    }
                    return;
                }

                // Extract ONLY the item name, excluding any rarity text
                // The HTML structure is: <b>Item Name<br><span class="medText"><b><font color='green'>(uncommon)</font></b><br></span></b>
                // We want only the text directly in the <b> element before any <br> or <span>
                let name = '';

                // Get all child nodes of the <b> element
                const childNodes = nameEl.childNodes;
                for (let node of childNodes) {
                    if (node.nodeType === Node.TEXT_NODE) {
                        // This is a text node - add it to the name
                        name += node.textContent;
                    } else if (node.nodeName === 'BR') {
                        // Stop at the first <br> - everything after is rarity info
                        break;
                    }
                }

                name = name.trim();

                const imageUrl = img.src;

                // Try to find the quantity cell
                // Look for the cell with just numeric content
                let quantity = 1;
                let qtyFound = false;

                // Search through cells for quantity (usually cell 4 or 5)
                for (let i = 4; i < cells.length && i < 7; i++) {
                    const cellText = cells[i].textContent.trim();

                    // Check if this looks like a quantity (just a number)
                    if (/^\d+$/.test(cellText)) {
                        quantity = parseInt(cellText, 10);
                        qtyFound = true;
                        if (rowIndex < 3) {
                            console.log(`Row ${rowIndex}: Found qty ${quantity} in cell ${i}`);
                        }
                        break;
                    }
                }

                if (!qtyFound && rowIndex < 3) {
                    console.log(`Row ${rowIndex}: No quantity found, defaulting to 1`);
                }

                if (rowIndex < 3) {
                    console.log(`Row ${rowIndex}: Extracted name: "${name}"`);
                }

                items.push({
                    name: name,
                    quantity: quantity,
                    imageUrl: imageUrl,
                    source: 'sdb'
                });
            });

            console.log(`=== TOTAL ITEMS PARSED: ${items.length} ===`);
            return items;
        }

        clearAllData() {
            console.log('Clearing all cached data...');
            this.inventory = {
                gallery: [],
                inventory: [],
                shop: [],
                sdb: [],
                lastUpdated: {
                    gallery: null,
                    inventory: null,
                    shop: null,
                    sdb: null
                }
            };
            this.priceCache = {};
            this.save();
            console.log('All data cleared!');
        }

        getAllItems() {
            const allItems = {};

            ['gallery', 'shop', 'inventory', 'sdb'].forEach(source => {
                this.inventory[source].forEach(item => {
                    const key = item.name;
                    if (!allItems[key]) {
                        allItems[key] = {
                            name: item.name,
                            imageUrl: item.imageUrl,
                            sources: {},
                            totalQuantity: 0
                        };
                    }
                    allItems[key].sources[source] = item.quantity;
                    allItems[key].totalQuantity += item.quantity;
                });
            });

            return Object.values(allItems);
        }

        async fetchItemPrice(itemName, forceUpdate = false) {
            // Check cache
            const cached = this.priceCache[itemName];
            if (!forceUpdate && cached && (Date.now() - cached.timestamp) < CONFIG.CACHE_DURATION) {
                console.log(`Using cached price for ${itemName}`);
                return cached.data;
            }

            console.log(`Fetching fresh price for ${itemName} from ItemDB API...`);

            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: CONFIG.ITEMDB_API + encodeURIComponent(itemName),
                    onload: (response) => {
                        try {
                            const data = JSON.parse(response.responseText);

                            const priceData = {
                                item_id: data.item_id,
                                price: data.price?.value || 0,
                                rarity: data.rarity,
                                category: data.category,
                                last_updated: data.price?.addedAt,
                                timestamp: Date.now()
                            };

                            this.priceCache[itemName] = {
                                data: priceData,
                                timestamp: Date.now()
                            };
                            this.save();

                            console.log(`Successfully fetched price for ${itemName}: ${priceData.price} NP`);
                            resolve(priceData);
                        } catch (error) {
                            console.error('Error parsing ItemDB response:', error);
                            reject(error);
                        }
                    },
                    onerror: reject
                });
            });
        }

        async updateAllPrices(forceUpdate = false, progressCallback) {
            const allItems = this.getAllItems();
            const results = [];

            // Get list of items that need prices
            let itemsToFetch = [];
            let cachedItems = [];

            for (let item of allItems) {
                const cached = this.priceCache[item.name];
                const isCached = cached && (Date.now() - cached.timestamp) < CONFIG.CACHE_DURATION;

                if (!forceUpdate && isCached) {
                    // Use cached data
                    cachedItems.push({
                        ...item,
                        ...cached.data,
                        updated: false
                    });
                } else {
                    itemsToFetch.push(item);
                }
            }

            console.log(`Items to fetch: ${itemsToFetch.length}, Cached: ${cachedItems.length}`);

            if (itemsToFetch.length === 0) {
                console.log('All items already cached!');
                if (progressCallback) {
                    progressCallback(allItems.length, allItems.length, 'All cached', true);
                }
                return [...cachedItems];
            }

            // Fetch prices in batches (ItemDB API limit appears to be around 500-1000 items)
            const BATCH_SIZE = 500;
            const batches = [];

            for (let i = 0; i < itemsToFetch.length; i += BATCH_SIZE) {
                batches.push(itemsToFetch.slice(i, i + BATCH_SIZE));
            }

            console.log(`Splitting ${itemsToFetch.length} items into ${batches.length} batches of ${BATCH_SIZE}`);

            try {
                for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
                    const batch = batches[batchIndex];
                    const batchNum = batchIndex + 1;

                    console.log(`Fetching batch ${batchNum}/${batches.length} (${batch.length} items)...`);

                    if (progressCallback) {
                        progressCallback(
                            batchIndex * BATCH_SIZE + batch.length,
                            itemsToFetch.length,
                            `Batch ${batchNum}/${batches.length}`,
                            false
                        );
                    }

                    const fetchedData = await this.fetchBulkPrices(batch.map(i => i.name));

                    // Process results for this batch
                    for (let item of batch) {
                        const priceData = fetchedData[item.name];

                        if (priceData) {
                            const processedData = {
                                item_id: priceData.item_id,
                                price: priceData.price?.value || 0,
                                rarity: priceData.rarity,
                                category: priceData.category,
                                status: priceData.status,
                                isNC: priceData.isNC,
                                owls: priceData.owls,
                                last_updated: priceData.price?.addedAt,
                                timestamp: Date.now()
                            };

                            // Cache it
                            this.priceCache[item.name] = {
                                data: processedData,
                                timestamp: Date.now()
                            };

                            results.push({
                                ...item,
                                ...processedData,
                                updated: true
                            });
                        } else {
                            console.warn(`No price data found for ${item.name}`);
                            results.push({
                                ...item,
                                updated: false
                            });
                        }
                    }

                    // Save after each batch
                    this.save();

                    // Small delay between batches to be respectful
                    if (batchIndex < batches.length - 1) {
                        await new Promise(resolve => setTimeout(resolve, 1000));
                    }
                }

                console.log(`Bulk fetch complete: ${results.length} updated, ${cachedItems.length} cached`);
                return [...results, ...cachedItems];

            } catch (error) {
                console.error('Error in bulk price fetch:', error);
                throw error;
            }
        }

        async fetchBulkPrices(itemNames) {
            console.log(`Fetching bulk prices for ${itemNames.length} items...`);

            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: 'https://itemdb.com.br/api/v1/items/many',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify({ name: itemNames }),
                    onload: (response) => {
                        try {
                            if (response.status === 200) {
                                const data = JSON.parse(response.responseText);
                                console.log(`Successfully fetched ${Object.keys(data).length} item prices from ItemDB`);
                                resolve(data);
                            } else {
                                console.error('ItemDB API returned error:', response.status, response.responseText);
                                reject(new Error(`API returned status ${response.status}`));
                            }
                        } catch (error) {
                            console.error('Error parsing ItemDB bulk response:', error);
                            reject(error);
                        }
                    },
                    onerror: (error) => {
                        console.error('Network error fetching bulk prices:', error);
                        reject(error);
                    }
                });
            });
        }
    }

    // UI Creation
    class InventoryUI {
        constructor(manager) {
            this.manager = manager;
            this.isOpen = false;
            this.currentPage = 1;
            this.itemsPerPage = 50;
            this.filteredItems = [];
        }

        createPanel() {
            const panel = document.createElement('div');
            panel.id = 'inventory-manager-panel';
            panel.innerHTML = `
                <style>
                    #inventory-manager-panel {
                        position: fixed;
                        top: 50%;
                        left: 50%;
                        transform: translate(-50%, -50%);
                        width: 90%;
                        max-width: 1200px;
                        height: 80vh;
                        background: white;
                        border: 3px solid #4a90e2;
                        border-radius: 10px;
                        box-shadow: 0 4px 20px rgba(0,0,0,0.3);
                        z-index: 10000;
                        display: none;
                        flex-direction: column;
                    }
                    #inventory-manager-panel.open {
                        display: flex;
                    }
                    .im-header {
                        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                        color: white;
                        padding: 15px 20px;
                        border-radius: 7px 7px 0 0;
                        display: flex;
                        justify-content: space-between;
                        align-items: center;
                    }
                    .im-header h2 {
                        margin: 0;
                        font-size: 24px;
                    }
                    .im-close {
                        background: rgba(255,255,255,0.2);
                        border: none;
                        color: white;
                        font-size: 24px;
                        cursor: pointer;
                        width: 30px;
                        height: 30px;
                        border-radius: 50%;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                    }
                    .im-close:hover {
                        background: rgba(255,255,255,0.3);
                    }
                    .im-controls {
                        padding: 15px 20px;
                        background: #f5f5f5;
                        border-bottom: 1px solid #ddd;
                        display: flex;
                        gap: 10px;
                        flex-wrap: wrap;
                    }
                    .im-btn {
                        padding: 8px 16px;
                        border: none;
                        border-radius: 5px;
                        cursor: pointer;
                        font-weight: bold;
                        transition: all 0.3s;
                    }
                    .im-btn-primary {
                        background: #4a90e2;
                        color: white;
                    }
                    .im-btn-primary:hover {
                        background: #357abd;
                    }
                    .im-btn-success {
                        background: #5cb85c;
                        color: white;
                    }
                    .im-btn-success:hover {
                        background: #449d44;
                    }
                    .im-btn-warning {
                        background: #f0ad4e;
                        color: white;
                    }
                    .im-btn-warning:hover {
                        background: #ec971f;
                    }
                    .im-btn-danger {
                        background: #dc3545;
                        color: white;
                    }
                    .im-btn-danger:hover {
                        background: #c82333;
                    }
                    .im-pagination-controls {
                        padding: 15px 20px;
                        background: #f8f9fa;
                        border-bottom: 1px solid #ddd;
                        display: flex;
                        justify-content: space-between;
                        align-items: center;
                        flex-wrap: wrap;
                        gap: 15px;
                    }
                    .im-per-page {
                        display: flex;
                        align-items: center;
                        gap: 10px;
                    }
                    .im-per-page label {
                        font-weight: 600;
                        color: #333;
                    }
                    .im-select {
                        padding: 6px 12px;
                        border: 1px solid #ddd;
                        border-radius: 4px;
                        background: white;
                        font-size: 14px;
                        cursor: pointer;
                    }
                    .im-select:hover {
                        border-color: #0066cc;
                    }
                    .im-pagination {
                        display: flex;
                        gap: 5px;
                        align-items: center;
                    }
                    .im-page-btn {
                        padding: 6px 12px;
                        border: 1px solid #ddd;
                        border-radius: 4px;
                        background: white;
                        cursor: pointer;
                        font-size: 14px;
                        color: #333;
                        transition: all 0.2s;
                    }
                    .im-page-btn:hover:not(:disabled) {
                        background: #0066cc;
                        color: white;
                        border-color: #0066cc;
                    }
                    .im-page-btn:disabled {
                        opacity: 0.5;
                        cursor: not-allowed;
                    }
                    .im-page-btn.active {
                        background: #0066cc;
                        color: white;
                        border-color: #0066cc;
                        font-weight: bold;
                    }
                    .im-page-info {
                        font-size: 14px;
                        color: #666;
                        margin: 0 10px;
                    }
                    .im-stats {
                        padding: 10px 20px;
                        background: #e8f4f8;
                        border-bottom: 1px solid #ddd;
                        display: flex;
                        justify-content: space-around;
                        flex-wrap: wrap;
                    }
                    .im-stat {
                        text-align: center;
                        padding: 5px 15px;
                    }
                    .im-stat-value {
                        font-size: 24px;
                        font-weight: bold;
                        color: #4a90e2;
                    }
                    .im-stat-label {
                        font-size: 12px;
                        color: #666;
                    }
                    .im-content {
                        flex: 1;
                        overflow-y: auto;
                        padding: 20px;
                    }
                    .im-table {
                        width: 100%;
                        border-collapse: collapse;
                        background: white;
                    }
                    .im-table th {
                        background: #667eea;
                        color: white;
                        padding: 10px;
                        text-align: left;
                        position: sticky;
                        top: 0;
                        z-index: 10;
                    }
                    .im-table td {
                        padding: 8px;
                        border-bottom: 1px solid #eee;
                    }
                    .im-table tr:hover {
                        background: #f9f9f9;
                    }
                    .im-item-img {
                        width: 40px;
                        height: 40px;
                        object-fit: contain;
                    }
                    .im-source-badge {
                        display: inline-block;
                        padding: 2px 8px;
                        border-radius: 3px;
                        font-size: 11px;
                        margin: 2px;
                        font-weight: bold;
                    }
                    .im-source-gallery { background: #d4edda; color: #155724; }
                    .im-source-shop { background: #fff3cd; color: #856404; }
                    .im-source-inventory { background: #d1ecf1; color: #0c5460; }
                    .im-source-sdb { background: #f8d7da; color: #721c24; }
                    .im-progress {
                        margin: 10px 0;
                        padding: 10px;
                        background: #e8f4f8;
                        border-radius: 5px;
                        display: none;
                    }
                    .im-progress.active {
                        display: block;
                    }
                    .im-progress-bar {
                        width: 100%;
                        height: 20px;
                        background: #ddd;
                        border-radius: 10px;
                        overflow: hidden;
                    }
                    .im-progress-fill {
                        height: 100%;
                        background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
                        transition: width 0.3s;
                    }
                    .im-filter {
                        padding: 8px 12px;
                        border: 1px solid #ddd;
                        border-radius: 5px;
                        width: 200px;
                    }
                </style>

                <div class="im-header">
                    <h2>🎒 Inventory Manager</h2>
                    <button class="im-close" id="im-close-btn">×</button>
                </div>

                <div class="im-controls">
                    <button class="im-btn im-btn-primary" id="im-fetch-gallery">Fetch Gallery</button>
                    <button class="im-btn im-btn-primary" id="im-fetch-inventory">Fetch Inventory</button>
                    <button class="im-btn im-btn-primary" id="im-fetch-shop">Fetch Shop</button>
                    <button class="im-btn im-btn-primary" id="im-fetch-sdb">Fetch SDB</button>
                    <button class="im-btn im-btn-success" id="im-fetch-all">Fetch All</button>
                    <button class="im-btn im-btn-warning" id="im-update-prices">Update Prices (New Only)</button>
                    <button class="im-btn im-btn-warning" id="im-force-update-prices">Force Update All Prices</button>
                    <button class="im-btn im-btn-danger" id="im-clear-cache">Clear All Cache</button>
                    <input type="text" class="im-filter" id="im-filter-input" placeholder="Filter items...">
                </div>

                <div class="im-pagination-controls">
                    <div class="im-per-page">
                        <label for="im-per-page-select">Items per page:</label>
                        <select id="im-per-page-select" class="im-select">
                            <option value="25">25</option>
                            <option value="50" selected>50</option>
                            <option value="100">100</option>
                            <option value="250">250</option>
                            <option value="500">500</option>
                            <option value="all">Show All</option>
                        </select>
                    </div>
                    <div class="im-pagination" id="im-pagination">
                        <!-- Pagination buttons will be inserted here -->
                    </div>
                </div>

                <div class="im-progress" id="im-progress">
                    <div id="im-progress-text">Processing...</div>
                    <div class="im-progress-bar">
                        <div class="im-progress-fill" id="im-progress-fill" style="width: 0%"></div>
                    </div>
                </div>

                <div class="im-stats">
                    <div class="im-stat">
                        <div class="im-stat-value" id="im-stat-total">0</div>
                        <div class="im-stat-label">Total Items</div>
                    </div>
                    <div class="im-stat">
                        <div class="im-stat-value" id="im-stat-unique">0</div>
                        <div class="im-stat-label">Unique Items</div>
                    </div>
                    <div class="im-stat">
                        <div class="im-stat-value" id="im-stat-value">0 NP</div>
                        <div class="im-stat-label">Total Value</div>
                    </div>
                </div>

                <div class="im-content">
                    <table class="im-table">
                        <thead>
                            <tr>
                                <th>Image</th>
                                <th>Item Name</th>
                                <th>Total Qty</th>
                                <th>Sources</th>
                                <th>Price</th>
                                <th>Value</th>
                                <th>Rarity</th>
                            </tr>
                        </thead>
                        <tbody id="im-table-body">
                            <tr><td colspan="7" style="text-align: center; padding: 40px;">No data loaded. Click "Fetch All" to begin.</td></tr>
                        </tbody>
                    </table>
                </div>
            `;

            document.body.appendChild(panel);
            this.attachEventListeners();
        }

        attachEventListeners() {
            document.getElementById('im-close-btn').addEventListener('click', () => this.close());
            document.getElementById('im-fetch-gallery').addEventListener('click', () => this.fetchSource('gallery'));
            document.getElementById('im-fetch-inventory').addEventListener('click', () => this.fetchSource('inventory'));
            document.getElementById('im-fetch-shop').addEventListener('click', () => this.fetchSource('shop'));
            document.getElementById('im-fetch-sdb').addEventListener('click', () => this.fetchSource('sdb'));
            document.getElementById('im-fetch-all').addEventListener('click', () => this.fetchAll());
            document.getElementById('im-update-prices').addEventListener('click', () => this.updatePrices(false));
            document.getElementById('im-force-update-prices').addEventListener('click', () => this.updatePrices(true));
            document.getElementById('im-clear-cache').addEventListener('click', () => this.clearCache());
            document.getElementById('im-filter-input').addEventListener('input', (e) => this.filterItems(e.target.value));
            document.getElementById('im-per-page-select').addEventListener('change', (e) => this.changeItemsPerPage(e.target.value));
        }

        async fetchSource(source) {
            this.showProgress(true, `Fetching from ${source}...`);

            try {
                let items = [];

                const progressCallback = (current, total, message) => {
                    document.getElementById('im-progress-text').textContent =
                        `Fetching from ${source}... Page ${current} (${message})`;
                };

                switch(source) {
                    case 'gallery':
                        console.log('Starting gallery fetch...');
                        items = await this.manager.fetchFromGallery();
                        console.log(`Gallery fetch complete: ${items.length} items`);
                        break;
                    case 'inventory':
                        console.log('Starting inventory fetch...');
                        items = await this.manager.fetchFromInventory();
                        console.log(`Inventory fetch complete: ${items.length} items`);
                        break;
                    case 'shop':
                        console.log('Starting shop fetch...');
                        items = await this.manager.fetchFromShop(progressCallback);
                        console.log(`Shop fetch complete: ${items.length} items`);
                        break;
                    case 'sdb':
                        console.log('Starting SDB fetch...');
                        items = await this.manager.fetchFromSDB(progressCallback);
                        console.log(`SDB fetch complete: ${items.length} items`);
                        break;
                }

                this.refreshDisplay();

                if (items.length === 0) {
                    alert(`Fetched from ${source}, but found 0 items. This might mean:\n\n1. The ${source} is empty\n2. You need to navigate to the ${source} page first\n3. The page structure has changed\n\nCheck the browser console (F12) for more details.`);
                } else {
                    alert(`Successfully fetched ${items.length} items from ${source}!`);
                }
            } catch (error) {
                console.error(`Error fetching ${source}:`, error);
                alert(`Error fetching from ${source}. Check console (F12) for details.\n\nError: ${error.message || error}`);
            } finally {
                this.showProgress(false);
            }
        }

        async fetchAll() {
            this.showProgress(true, 'Fetching all sources...');

            try {
                const progressCallback = (current, total, message) => {
                    document.getElementById('im-progress-text').textContent = message;
                };

                document.getElementById('im-progress-text').textContent = 'Fetching Gallery...';
                await this.manager.fetchFromGallery();
                this.updateProgressBar(25);

                document.getElementById('im-progress-text').textContent = 'Fetching Inventory...';
                await this.manager.fetchFromInventory();
                this.updateProgressBar(50);

                document.getElementById('im-progress-text').textContent = 'Fetching Shop...';
                await this.manager.fetchFromShop(progressCallback);
                this.updateProgressBar(75);

                document.getElementById('im-progress-text').textContent = 'Fetching SDB...';
                await this.manager.fetchFromSDB(progressCallback);
                this.updateProgressBar(100);

                this.refreshDisplay();
                alert('Successfully fetched all sources!');
            } catch (error) {
                console.error('Error fetching all:', error);
                alert('Error fetching all sources. Check console for details.');
            } finally {
                this.showProgress(false);
            }
        }

        async updatePrices(forceUpdate = false) {
            const buttonText = forceUpdate ? 'Force updating all prices' : 'Updating new prices only';
            this.showProgress(true, `${buttonText} from ItemDB...`);

            try {
                const items = await this.manager.updateAllPrices(forceUpdate, (current, total, itemName, skipped) => {
                    const percent = (current / total) * 100;
                    this.updateProgressBar(percent);
                    const skipText = skipped ? ' (skipped - already cached)' : '';
                    document.getElementById('im-progress-text').textContent =
                        `${buttonText}... ${current}/${total} (${itemName})${skipText}`;
                });

                this.refreshDisplay();

                const updatedCount = items.filter(item => item.updated).length;
                const skippedCount = items.length - updatedCount;

                let message = `Price update complete!\n\n`;
                message += `Updated: ${updatedCount} items\n`;
                if (!forceUpdate && skippedCount > 0) {
                    message += `Skipped: ${skippedCount} items (already cached)\n\n`;
                    message += `Use "Force Update All Prices" to refresh all cached prices.`;
                }

                alert(message);
            } catch (error) {
                console.error('Error updating prices:', error);
                alert('Error updating prices. Check console for details.');
            } finally {
                this.showProgress(false);
            }
        }

        refreshDisplay() {
            const allItems = this.manager.getAllItems();
            this.filteredItems = allItems; // Store all items for pagination
            this.currentPage = 1; // Reset to page 1
            this.renderTable();
        }

        renderTable() {
            const tbody = document.getElementById('im-table-body');

            if (this.filteredItems.length === 0) {
                tbody.innerHTML = '<tr><td colspan="7" style="text-align: center; padding: 40px;">No items found. Fetch from sources first.</td></tr>';
                this.renderPagination();
                return;
            }

            // Get prices from cache
            const itemsWithPrices = this.filteredItems.map(item => {
                const cached = this.manager.priceCache[item.name];
                const priceData = cached?.data || {};

                // Handle different price scenarios
                let displayPrice = 0;
                let priceDisplay = '???';

                if (priceData.status === 'no trade') {
                    priceDisplay = '<span style="color: #cc0000;">No Trade</span>';
                } else if (priceData.isNC && !priceData.owls) {
                    priceDisplay = '<span style="color: #0066cc;">NC</span>';
                } else if (priceData.isNC && priceData.owls) {
                    priceDisplay = `<span style="color: #0066cc;">${priceData.owls.value || 'NC'}</span>`;
                } else if (priceData.price) {
                    displayPrice = priceData.price;
                    priceDisplay = displayPrice.toLocaleString() + ' NP';
                }

                const totalValue = displayPrice * item.totalQuantity;

                return {
                    ...item,
                    price: displayPrice,
                    priceDisplay: priceDisplay,
                    rarity: priceData.rarity || '?',
                    status: priceData.status,
                    isNC: priceData.isNC,
                    totalValue: totalValue
                };
            });

            // Sort by total value descending
            itemsWithPrices.sort((a, b) => b.totalValue - a.totalValue);

            // Calculate pagination
            const startIndex = (this.currentPage - 1) * this.itemsPerPage;
            const endIndex = this.itemsPerPage === 'all' ? itemsWithPrices.length : startIndex + parseInt(this.itemsPerPage);
            const itemsToShow = itemsWithPrices.slice(startIndex, endIndex);

            tbody.innerHTML = itemsToShow.map(item => `
                <tr>
                    <td><img src="${item.imageUrl}" class="im-item-img" alt="${item.name}"></td>
                    <td><strong>${item.name}</strong></td>
                    <td>${item.totalQuantity.toLocaleString()}</td>
                    <td>
                        ${Object.entries(item.sources).map(([source, qty]) =>
                            `<span class="im-source-badge im-source-${source}">${source}: ${qty}</span>`
                        ).join('')}
                    </td>
                    <td>${item.priceDisplay}</td>
                    <td><strong>${item.totalValue > 0 ? item.totalValue.toLocaleString() + ' NP' : '-'}</strong></td>
                    <td>r${item.rarity}</td>
                </tr>
            `).join('');

            this.updateStats(itemsWithPrices);
            this.renderPagination();
        }

        renderPagination() {
            const paginationDiv = document.getElementById('im-pagination');

            if (this.itemsPerPage === 'all' || this.filteredItems.length === 0) {
                paginationDiv.innerHTML = '';
                return;
            }

            const totalPages = Math.ceil(this.filteredItems.length / this.itemsPerPage);

            if (totalPages <= 1) {
                paginationDiv.innerHTML = '';
                return;
            }

            let html = '';

            // Previous button
            html += `<button class="im-page-btn" ${this.currentPage === 1 ? 'disabled' : ''} data-page="${this.currentPage - 1}">« Prev</button>`;

            // Page numbers
            const maxButtons = 7;
            let startPage = Math.max(1, this.currentPage - Math.floor(maxButtons / 2));
            let endPage = Math.min(totalPages, startPage + maxButtons - 1);

            if (endPage - startPage < maxButtons - 1) {
                startPage = Math.max(1, endPage - maxButtons + 1);
            }

            if (startPage > 1) {
                html += `<button class="im-page-btn" data-page="1">1</button>`;
                if (startPage > 2) {
                    html += `<span class="im-page-info">...</span>`;
                }
            }

            for (let i = startPage; i <= endPage; i++) {
                html += `<button class="im-page-btn ${i === this.currentPage ? 'active' : ''}" data-page="${i}">${i}</button>`;
            }

            if (endPage < totalPages) {
                if (endPage < totalPages - 1) {
                    html += `<span class="im-page-info">...</span>`;
                }
                html += `<button class="im-page-btn" data-page="${totalPages}">${totalPages}</button>`;
            }

            // Next button
            html += `<button class="im-page-btn" ${this.currentPage === totalPages ? 'disabled' : ''} data-page="${this.currentPage + 1}">Next »</button>`;

            // Page info
            const startItem = (this.currentPage - 1) * this.itemsPerPage + 1;
            const endItem = Math.min(this.currentPage * this.itemsPerPage, this.filteredItems.length);
            html += `<span class="im-page-info">Showing ${startItem}-${endItem} of ${this.filteredItems.length}</span>`;

            paginationDiv.innerHTML = html;

            // Attach click events to pagination buttons
            paginationDiv.querySelectorAll('.im-page-btn').forEach(btn => {
                btn.addEventListener('click', (e) => {
                    const page = parseInt(e.target.dataset.page);
                    if (page && page !== this.currentPage) {
                        this.goToPage(page);
                    }
                });
            });
        }

        goToPage(page) {
            this.currentPage = page;
            this.renderTable();
            // Scroll to top of table
            document.getElementById('im-table-body').scrollIntoView({ behavior: 'smooth', block: 'start' });
        }

        changeItemsPerPage(value) {
            this.itemsPerPage = value === 'all' ? 'all' : parseInt(value);
            this.currentPage = 1; // Reset to first page
            this.renderTable();
        }

        updateStats(items) {
            const totalItems = items.reduce((sum, item) => sum + item.totalQuantity, 0);
            const uniqueItems = items.length;
            const totalValue = items.reduce((sum, item) => sum + item.totalValue, 0);

            document.getElementById('im-stat-total').textContent = totalItems.toLocaleString();
            document.getElementById('im-stat-unique').textContent = uniqueItems.toLocaleString();
            document.getElementById('im-stat-value').textContent = totalValue.toLocaleString() + ' NP';
        }

        clearCache() {
            if (!confirm('Are you sure you want to clear ALL cached data?\n\nThis will:\n• Clear all item data (Gallery, Inventory, Shop, SDB)\n• Clear all cached prices\n• You will need to re-fetch everything\n\nThis action cannot be undone!')) {
                return;
            }

            try {
                this.manager.clearAllData();
                this.refreshDisplay();
                alert('All cached data has been cleared!\n\nYou can now re-fetch your items with clean data.');
            } catch (error) {
                console.error('Error clearing cache:', error);
                alert('Error clearing cache. Check console for details.');
            }
        }

        filterItems(query) {
            const allItems = this.manager.getAllItems();
            const lowerQuery = query.toLowerCase();

            if (!query.trim()) {
                this.filteredItems = allItems;
            } else {
                this.filteredItems = allItems.filter(item =>
                    item.name.toLowerCase().includes(lowerQuery)
                );
            }

            this.currentPage = 1; // Reset to first page when filtering
            this.renderTable();
        }

        showProgress(show, text = '') {
            const progress = document.getElementById('im-progress');
            if (show) {
                progress.classList.add('active');
                document.getElementById('im-progress-text').textContent = text;
                this.updateProgressBar(0);
            } else {
                progress.classList.remove('active');
            }
        }

        updateProgressBar(percent) {
            document.getElementById('im-progress-fill').style.width = percent + '%';
        }

        open() {
            document.getElementById('inventory-manager-panel').classList.add('open');
            this.isOpen = true;
            this.refreshDisplay();
        }

        close() {
            document.getElementById('inventory-manager-panel').classList.remove('open');
            this.isOpen = false;
        }

        toggle() {
            if (this.isOpen) {
                this.close();
            } else {
                this.open();
            }
        }
    }

    // Create floating button
    function createFloatingButton(ui) {
        const button = document.createElement('button');
        button.id = 'inventory-manager-float-btn';
        button.innerHTML = '🎒';
        button.style.cssText = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            width: 60px;
            height: 60px;
            border-radius: 50%;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            font-size: 28px;
            cursor: pointer;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            z-index: 9999;
            transition: transform 0.3s;
        `;

        button.addEventListener('mouseenter', () => {
            button.style.transform = 'scale(1.1)';
        });

        button.addEventListener('mouseleave', () => {
            button.style.transform = 'scale(1)';
        });

        button.addEventListener('click', () => ui.toggle());

        document.body.appendChild(button);
    }

    // Initialize
    function init() {
        // Wait for page to load
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', init);
            return;
        }

        const manager = new InventoryManager();
        const ui = new InventoryUI(manager);

        // Check if we're on the stamps dashboard page
        const isDashboardPage = window.location.href.includes('/stamps.phtml?type=dashboard');

        if (isDashboardPage) {
            // Replace the entire content area with our panel
            replacePageContent(ui);
        } else {
            // Normal mode - create floating button
            ui.createPanel();
            createFloatingButton(ui);
        }

        // Auto-fetch if on gallery page
        if (window.location.href.includes('/gallery/')) {
            setTimeout(() => {
                manager.fetchFromGallery().then(() => {
                    console.log('Auto-fetched gallery items');
                });
            }, 1000);
        }

        console.log('Neopets Inventory Manager loaded! Click the 🎒 button to open.');
    }

    // Replace page content for dashboard mode
    function replacePageContent(ui) {
        // Find the main content area
        const contentArea = document.querySelector('td.content');

        if (contentArea) {
            // Clear existing content
            contentArea.innerHTML = '';

            // Create container for our dashboard
            const dashboardContainer = document.createElement('div');
            dashboardContainer.id = 'inventory-dashboard-container';
            dashboardContainer.innerHTML = `
                <style>
                    #inventory-dashboard-container {
                        width: 100%;
                        min-height: 600px;
                        padding: 20px;
                    }
                    #inventory-manager-panel.dashboard-mode {
                        position: relative !important;
                        top: auto !important;
                        left: auto !important;
                        transform: none !important;
                        width: 100% !important;
                        max-width: 100% !important;
                        height: auto !important;
                        min-height: 700px;
                        display: flex !important;
                        border: none;
                        border-radius: 10px;
                        box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                    }
                    #inventory-manager-panel.dashboard-mode .im-header {
                        border-radius: 10px 10px 0 0;
                    }
                    #inventory-manager-panel.dashboard-mode .im-close {
                        display: none;
                    }
                    #inventory-manager-panel.dashboard-mode .im-content {
                        max-height: none;
                    }
                </style>
            `;

            contentArea.appendChild(dashboardContainer);

            // Create the panel
            ui.createPanel();

            // Add dashboard-mode class and make it visible
            const panel = document.getElementById('inventory-manager-panel');
            panel.classList.add('dashboard-mode');
            panel.classList.add('open');

            // Move panel into our container
            dashboardContainer.appendChild(panel);

            // Load data immediately
            ui.refreshDisplay();

            console.log('Dashboard mode activated - Inventory Manager is now the main page content');
        }
    }

    init();
})();