Neon Game Hub

Game search tool directly from the Steam game page to your favourite websites search engine!

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Neon Game Hub
// @namespace    https://violentmonkey.github.io/
// @version      3.7
// @description  Game search tool directly from the Steam game page to your favourite websites search engine!
// @author       Okagame
// @license      MIT
// @match        https://store.steampowered.com/app/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @connect      self
// @run-at       document-end
// ==/UserScript==
(function() {
    'use strict';
    console.log("⚡ Neon Game Hub is on! ⚡");
    // ======================
    // CONFIGURATION
    // ======================
    const CONFIG = {
        colors: {
            primary: 'rgba(10, 5, 15, 0.95)',
            accent: '#00ff9d',
            accentLight: '#00ffaa',
            text: '#ff99cc',
            border: 'rgba(0, 255, 157, 0.3)',
            folderBg: 'rgba(15, 10, 25, 0.7)',
            favourite: '#ff4444',
            nonFavourite: '#333333'
        },
        storage: {
            FAVOURITES_KEY: 'ngsh_favourites'
        },
        SITE_DEFINITIONS: {
            'Direct Downloads': [
        { name: 'CS.RIN.RU', icon: '📥', urlPattern: 'https://cs.rin.ru/forum/search.php?keywords={query}&terms=any&author=&sc=1&sf=titleonly&sk=t&sd=d&sr=topics&st=0&ch=300&t=0&submit=Search' },
        { name: 'ElAmigos', icon: '📥', urlPattern: 'https://elamigos.site/?s={query}' },
        { name: 'FitGirl Repacks', icon: '📥', urlPattern: 'https://fitgirl-repacks.site/?s={query}' },
        { name: 'AtopGames', icon: '📥', urlPattern: 'https://atopgames.com/?s={query}' },
        { name: 'GOG-Games', icon: '📥', urlPattern: 'https://gog-games.to/?search={query}' },
        { name: 'G4U', icon: '📥', urlPattern: 'https://g4u.to/en/search/?str={query}' },
        { name: 'AbandonwareGames', icon: '📥', urlPattern: 'https://abandonwaregames.net/search.php?q={query}' },
        { name: 'AnkerGames', icon: '📥', urlPattern: 'https://ankergames.net/search/{query}' },
        { name: 'CG-GamesPC', icon: '📥', urlPattern: 'https://www.cg-gamespc.com/games?game={query}' },
        { name: 'GameBounty', icon: '📥', urlPattern: 'https://gamebounty.world/?s={query}' },
        { name: 'GamDie', icon: '📥', urlPattern: 'https://gamdie.com/?s={query}' },
        { name: 'GameDrive', icon: '📥', urlPattern: 'https://gamedrive.org/?s={query}' },
        { name: 'GamePCFull', icon: '📥', urlPattern: 'https://gamepcfull.com/?s={query}' },
        { name: 'Games4U', icon: '📥', urlPattern: 'https://games4u.org/?s={query}' },
        { name: 'GamesDrive', icon: '📥', urlPattern: 'https://gamesdrive.net/?s={query}' },
        { name: 'GamesPack', icon: '📥', urlPattern: 'https://games-pack.net/?s={query}' },
        { name: 'GameZDL', icon: '📥', urlPattern: 'https://gamezdl.cc/?s={query}' },
        { name: 'GetFreeGames', icon: '📥', urlPattern: 'https://getfreegames.net/?s={query}' },
        { name: 'Gnarly Repacks', icon: '📥', urlPattern: 'https://rentry.org/gnarly_repacks' },
        { name: 'M4ckD0ge Repacks', icon: '📥', urlPattern: 'https://m4ckd0ge-repacks.site/?s={query}' },
        { name: 'MyAbandonware', icon: '📥', urlPattern: 'https://www.myabandonware.com/search?q={query}' },
        { name: 'OldGamesDownload', icon: '📥', urlPattern: 'https://oldgamesdownload.com/?s={query}' },
        { name: 'OvaGames', icon: '📥', urlPattern: 'https://www.ovagames.com/?s={query}' },
        { name: 'ReloadedSteam', icon: '📥', urlPattern: 'https://reloadedsteam.com/?s={query}' },
        { name: 'Repack-Games', icon: '📥', urlPattern: 'https://repack-games.com/?s={query}' },
        { name: 'RepackLab', icon: '📥', urlPattern: 'https://repacklab.com/?s={query}' },
        { name: 'Steam-Cracked', icon: '📥', urlPattern: 'https://steam-cracked.com/?s={query}' },
        { name: 'SteamGG', icon: '📥', urlPattern: 'https://steamgg.net/?s={query}' },
        { name: 'SteamRip', icon: '📥', urlPattern: 'https://steamrip.com/?s={query}' },
        { name: 'SteamUnderground', icon: '📥', urlPattern: 'https://steamunderground.net/?s={query}' },
        { name: 'Collection Chamber', icon: '📥', urlPattern: 'https://collectionchamber.blogspot.com/search?q={query}' },
        { name: 'UndergroundGames', icon: '📥', urlPattern: 'https://undergroundgames.net/?s={query}' },
        { name: 'Union-Crax', icon: '📥', urlPattern: 'https://union-crax.xyz/?s={query}' },
        { name: 'Win7Games', icon: '📥', urlPattern: 'https://win7games.com/' },
        { name: 'WorldofPCGames', icon: '📥', urlPattern: 'https://worldofpcgames.com/?s={query}' }
            ],
            'Search Engines': [
        { name: 'Google Custom Search', icon: '🔍', urlPattern: 'https://cse.google.com/cse?cx=20c2a3e5f702049aa&q={query}' },
        { name: 'RaveGameSearch', icon: '🔍', urlPattern: 'https://ravegamesearch.pages.dev/search?q={query}' },
        { name: 'Rezi', icon: '🔍', urlPattern: 'https://rezi.one/?q={query}' },
        { name: 'Mr Pc Gamer', icon: '🔍', urlPattern: 'https://mrpcgamer.net/?s={query}' },
        { name: 'Game3rb', icon: '🔍', urlPattern: 'https://game3rb.com/?s={query}' }
            ],
            'Torrents': [
        { name: 'ByXatab', icon: '🌐', urlPattern: 'https://byxatab.com/?s={query}' },
        { name: 'Dodi-Repacks', icon: '🌐', urlPattern: 'https://dodi-repacks.site/?s={query}' },
        { name: 'FreeGOGPCGames', icon: '🌐', urlPattern: 'https://freegogpcgames.com/?s={query}' },
        { name: 'KaosKrew', icon: '🌐', urlPattern: 'https://kaoskrew.org/?s={query}' },
        { name: 'Torrent-Games.games', icon: '🌐', urlPattern: 'https://torrent-games.games/?s={query}' },
        { name: 'Torrent-Games.net', icon: '🌐', urlPattern: 'https://torrent-games.net/?s={query}' },
        { name: 'Appnetica', icon: '🌐', urlPattern: 'https://appnetica.com/search?term={query}' }
            ],
            'Utilities & Trainers': [
        { name: 'FLiNG Trainer', icon: '🔧', urlPattern: 'https://flingtrainer.com/?s={query}' },
        { name: 'GameCopyWorld', icon: '🔧', urlPattern: 'https://gamecopyworld.eu/games/search_results.shtml?q={query}&sa=%C2%A0+Google+Search%C2%A0+' },
        { name: 'MegaGames', icon: '🔧', urlPattern: 'https://megagames.com/results?query={query}' },
        { name: 'MrAntiFun', icon: '🔧', urlPattern: 'https://mrantifun.net/search/16823170/?q={query}&o=date' },
        { name: 'WeMod', icon: '🔧', urlPattern: 'https://www.wemod.com/cheats?q={query}' },
            ]
        }
    };
    // ======================
    // STORAGE MANAGER
    // ======================
    class StorageManager {
        static get(key, defaultValue = null) {
            try {
                const saved = localStorage.getItem(key);
                return saved ? JSON.parse(saved) : defaultValue;
            } catch (e) {
                console.warn(`Couldn't load ${key}:`, e);
                return defaultValue;
            }
        }
        static set(key, value) {
            try {
                localStorage.setItem(key, JSON.stringify(value));
                return true;
            } catch (e) {
                console.warn(`Couldn't save ${key}:`, e);
                return false;
            }
        }
    }
    // ======================
    // FAVOURITES MANAGER
    // ======================
    class FavouritesManager {
        constructor() {
            this.favourites = StorageManager.get(CONFIG.storage.FAVOURITES_KEY, []);
        }
        isFavourite(site) {
            return this.favourites.some(fav =>
                fav.name === site.name && fav.urlPattern === site.urlPattern
            );
        }
        toggle(site) {
            const index = this.favourites.findIndex(fav =>
                fav.name === site.name && fav.urlPattern === site.urlPattern
            );
            if (index >= 0) {
                this.favourites.splice(index, 1);
            } else {
                this.favourites.push(site);
            }
            StorageManager.set(CONFIG.storage.FAVOURITES_KEY, this.favourites);
            return this.favourites;
        }
        getAll() {
            const allSites = Object.values(CONFIG.SITE_DEFINITIONS).flat();
            return allSites.filter(site => this.isFavourite(site));
        }
        openAll() {
            const { gameName, appId } = GameInfoExtractor.getGameData();
            const favSites = this.getAll();
            if (favSites.length === 0) {
                NotificationManager.show("No favourite sites selected");
                return;
            }
            if (!confirm(`Open all ${favSites.length} favourite sites?`)) return;
            favSites.forEach(site => {
                SearchManager.performSearch(site, gameName, appId);
            });
        }
    }
    // ======================
    // GAME INFO EXTRACTOR
    // ======================
    class GameInfoExtractor {
        static getAppId() {
            const match = window.location.href.match(/app\/(\d+)/);
            return match ? match[1] : null;
        }
        static getGameName() {
            const url = window.location.href;
            if (url.includes('store.steampowered.com')) {
                const element = document.getElementById('appHubAppName');
                return element ? element.textContent.trim() : '';
            }
            return '';
        }
        static getDeveloper() {
            const url = window.location.href;
            if (url.includes('store.steampowered.com')) {
                const element = document.querySelector('.dev_row a');
                return element ? element.textContent.trim() : '';
            }
            return '';
        }
        static cleanGameName(gameName) {
            return gameName ? gameName.replace(/[™®©]/g, '').replace(/\s+/g, ' ').trim() : gameName;
        }
        static getGameData() {
            const appId = this.getAppId();
            const gameName = this.cleanGameName(this.getGameName());
            const developer = this.getDeveloper();
            return { appId, gameName, developer };
        }
    }
    // ======================
    // SEARCH MANAGER
    // ======================
    class SearchManager {
        static performSearch(site, query, appId) {
            const url = site.urlPattern
                .replace('{query}', encodeURIComponent(query))
                .replace('{appId}', appId || '');
            if (site.special) {
                const handler = SearchHandlers.getHandler(site.special);
                if (handler) {
                    const developer = GameInfoExtractor.getDeveloper();
                    handler(appId, query, developer, (foundUrl) => {
                        window.open(foundUrl, '_blank');
                    });
                    return;
                }
            }
            window.open(url, '_blank');
        }
    }
    // ======================
    // NOTIFICATION MANAGER
    // ======================
    class NotificationManager {
        static show(message, isError = false) {
            const existing = document.getElementById('ngsh-notification');
            if (existing) existing.remove();
            const notification = document.createElement('div');
            notification.id = 'ngsh-notification';
            notification.className = 'ngsh-notification';
            notification.textContent = message;
            notification.style.backgroundColor = isError ? '#ff4444' : 'rgba(0, 0, 0, 0.8)';
            document.body.appendChild(notification);
            setTimeout(() => {
                notification.style.opacity = '0';
                notification.style.transition = 'opacity 0.5s';
                setTimeout(() => notification.remove(), 500);
            }, 3000);
        }
    }
    // ======================
    // UI MANAGER
    // ======================
    class UIManager {
        constructor() {
            this.elements = {};
            this.menuVisible = false;
            this.favouritesManager = new FavouritesManager();
        }
        createStyles() {
            GM_addStyle(`
                .ngsh-container {
                    position: fixed;
                    z-index: 99999;
                    font-family: 'Segoe UI', sans-serif;
                    pointer-events: none;
                }
                #ngsh-main-button, #ngsh-quick-access {
                    position: fixed;
                    border-radius: 50%;
                    padding: 0;
                    font-weight: 600;
                    cursor: pointer;
                    transition: all 0.2s ease;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    pointer-events: auto;
                    z-index: 100000;
                }
                #ngsh-main-button {
                    right: 20px;
                    bottom: 40px;
                    background: linear-gradient(135deg, rgba(0, 255, 157, 0.7) 0%, rgba(0, 255, 180, 0.7) 100%);
                    color: #0a0005;
                    border: 1px solid rgba(0, 255, 157, 0.5);
                    font-size: 18px;
                    box-shadow: 0 4px 8px rgba(0, 255, 157, 0.2);
                    width: 48px;
                    height: 48px;
                }
                #ngsh-quick-access {
                    right: 70px;
                    bottom: 10px;
                    background: linear-gradient(135deg, rgba(0, 255, 157, 0.5) 0%, rgba(0, 255, 180, 0.5) 100%);
                    color: #0a0005;
                    border: 1px solid rgba(0, 255, 157, 0.3);
                    font-size: 14px;
                    box-shadow: 0 2px 4px rgba(0, 255, 157, 0.15);
                    width: 32px;
                    height: 32px;
                }
                #ngsh-main-button:hover, #ngsh-quick-access:hover {
                    transform: scale(1.1);
                    box-shadow: 0 6px 12px rgba(0, 255, 157, 0.3);
                }
                .ngsh-menu {
                    position: fixed;
                    right: 20px;
                    bottom: 100px;
                    width: 330px;
                    background: ${CONFIG.colors.primary};
                    border-radius: 10px;
                    box-shadow: 0 0 20px rgba(0, 255, 157, 0.3);
                    border: 1px solid ${CONFIG.colors.border};
                    backdrop-filter: blur(10px);
                    max-height: 80vh;
                    overflow: hidden;
                    pointer-events: auto;
                    z-index: 99999;
                    display: none;
                    overscroll-behavior: contain;
                }
                .ngsh-menu-header {
                    padding: 12px 20px;
                    color: ${CONFIG.colors.text};
                    font-size: 14px;
                    font-weight: 600;
                    text-transform: uppercase;
                    letter-spacing: 1px;
                    border-bottom: 1px solid ${CONFIG.colors.border};
                    background: linear-gradient(90deg, transparent 0%, rgba(0, 255, 157, 0.1) 100%);
                    flex-shrink: 0;
                }
                .ngsh-site-list {
                    max-height: calc(80vh - 200px);
                    overflow-y: auto;
                    padding: 8px;
                    scrollbar-width: none;
                    -ms-overflow-style: none;
                    overscroll-behavior: contain;
                }
                .ngsh-site-list::-webkit-scrollbar {
                    display: none;
                }
                .ngsh-search-box {
                    padding: 8px 15px;
                    margin: 8px;
                    background: rgba(15, 10, 25, 0.7);
                    border: 1px solid ${CONFIG.colors.border};
                    border-radius: 20px;
                    display: flex;
                    align-items: center;
                    position: sticky;
                    bottom: 0;
                    z-index: 10;
                }
                .ngsh-search-input {
                    flex: 1;
                    background: transparent;
                    border: none;
                    color: ${CONFIG.colors.text};
                    font-family: inherit;
                    font-size: 14px;
                    outline: none;
                    padding: 8px 0;
                }
                .ngsh-search-input::placeholder {
                    color: #ff99cc80;
                }
                .ngsh-search-clear, .ngsh-search-icon {
                    color: ${CONFIG.colors.accent};
                    cursor: pointer;
                    font-size: 16px;
                }
                .ngsh-search-clear {
                    margin-left: 8px;
                }
                .ngsh-search-icon {
                    margin-right: 8px;
                }
                .ngsh-folder {
                    margin-bottom: 8px;
                    border-radius: 6px;
                    overflow: hidden;
                    background: ${CONFIG.colors.folderBg};
                    border: 1px solid ${CONFIG.colors.border};
                    position: relative;
                    z-index: 5;
                }
                .ngsh-folder-header {
                    padding: 12px 15px;
                    display: flex;
                    align-items: center;
                    color: ${CONFIG.colors.text};
                    cursor: pointer;
                    border-bottom: 1px solid rgba(0, 255, 157, 0.1);
                    height: 15px;
                    z-index: 4;
                }
                .ngsh-folder-header:hover {
                    background: rgba(25, 15, 35, 0.7);
                    z-index: 4;
                }
                .ngsh-folder-icon {
                    margin-right: 10px;
                    transition: transform 0.3s ease;
                    font-size: 14px;
                    width: 16px;
                    text-align: center;
                    color: ${CONFIG.colors.accent};
                }
                .ngsh-folder.open .ngsh-folder-icon {
                    transform: rotate(90deg);
                }
                .ngsh-folder-title {
                    font-weight: 600;
                    font-size: 14px;
                    flex: 1;
                }
                .ngsh-folder-content {
                    max-height: 0;
                    overflow: hidden;
                    transition: max-height 0.3s ease;
                    position: relative;
                    overflow-y: auto;
                    overscroll-behavior: contain;
                }
                .ngsh-folder.open .ngsh-folder-content {
                    max-height: 300px;
                    overflow-y: auto;
                    scrollbar-width: none;
                    -ms-overflow-style: none;
                }
                .ngsh-folder-content::-webkit-scrollbar {
                    display: none;
                }
                .ngsh-menu-item {
                    padding: 12px 15px 12px 40px;
                    display: flex;
                    align-items: center;
                    color: ${CONFIG.colors.text};
                    cursor: pointer;
                    transition: all 0.2s ease;
                    margin: 2px 5px;
                    border-radius: 4px;
                    position: relative;
                }
                .ngsh-menu-item:hover {
                    background: rgba(0, 255, 157, 0.15);
                }
                .ngsh-site-icon {
                    width: 24px;
                    height: 24px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    margin-right: 10px;
                    color: ${CONFIG.colors.accent};
                    font-size: 16px;
                }
                .ngsh-favourite-icon {
                    position: absolute;
                    right: 8px;
                    top: 50%;
                    transform: translateY(-50%);
                    font-size: 18px;
                    cursor: pointer;
                    transition: all 0.2s ease;
                    z-index: 2;
                    width: 32px;
                    height: 32px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    border-radius: 50%;
                    background: rgba(0, 0, 0, 0.3);
                    border: 1px solid rgba(255, 255, 255, 0.1);
                }
                .ngsh-favourite-icon:hover {
                    background: rgba(0, 255, 157, 0.2);
                    border-color: rgba(0, 255, 157, 0.4);
                    transform: translateY(-50%) scale(1.1);
                    box-shadow: 0 2px 8px rgba(0, 255, 157, 0.3);
                }
                .ngsh-game-info {
                    padding: 10px 20px;
                    font-size: 13px;
                    color: #cc88aa;
                    border-top: 1px solid ${CONFIG.colors.border};
                    margin-top: 10px;
                    background: rgba(0, 0, 0, 0.2);
                    border-radius: 0 0 8px 8px;
                }
                .ngsh-open-all {
                    display: flex;
                    align-items: center;
                    padding: 10px 15px;
                    color: ${CONFIG.colors.text};
                    cursor: pointer;
                    background: rgba(0, 0, 0, 0.2);
                    margin: 5px;
                    border-radius: 4px;
                    border: 1px solid ${CONFIG.colors.border};
                }
                .ngsh-open-all:hover {
                    background: rgba(0, 255, 157, 0.15);
                }
                .ngsh-open-all-icon {
                    margin-right: 8px;
                    font-size: 16px;
                }
                .ngsh-notification {
                    position: fixed;
                    bottom: 52px;
                    right: 75px;
                    padding: 10px 15px;
                    border-radius: 4px;
                    background-color: rgba(0, 0, 0, 0.8);
                    color: ${CONFIG.colors.text};
                    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
                    z-index: 100000;
                    animation: fadeIn 0.3s;
                }
                @keyframes fadeIn {
                    from { opacity: 0; transform: translateY(10px); }
                    to { opacity: 1; transform: translateY(0); }
                }
            `);
        }
        createElement(tag, className = '', content = '') {
            const element = document.createElement(tag);
            if (className) element.className = className;
            if (content) element.textContent = content;
            return element;
        }
        createButtons() {
            // Main button
            this.elements.mainButton = this.createElement('button', '', '⚡');
            this.elements.mainButton.id = 'ngsh-main-button';
            // Quick access button
            this.elements.quickButton = this.createElement('button', '', '💖');
            this.elements.quickButton.id = 'ngsh-quick-access';
            this.elements.quickButton.title = 'Open all favourite sites';
            document.body.appendChild(this.elements.mainButton);
            document.body.appendChild(this.elements.quickButton);
            this.setupButtonEvents();
        }
        createMenu() {
            this.elements.menu = this.createElement('div', 'ngsh-menu');
            // Header
            const header = this.createElement('div', 'ngsh-menu-header', 'Search Hub');
            this.elements.menu.appendChild(header);
            // Site list
            this.elements.siteList = this.createElement('div', 'ngsh-site-list');
            this.elements.menu.appendChild(this.elements.siteList);
            // Search box
            this.createSearchBox();
            document.body.appendChild(this.elements.menu);
        }
        createSearchBox() {
            const searchBox = this.createElement('div', 'ngsh-search-box');
            const searchIcon = this.createElement('span', 'ngsh-search-icon', '🔍');
            const searchInput = this.createElement('input', 'ngsh-search-input');
            const searchClear = this.createElement('span', 'ngsh-search-clear', '✕');
            searchInput.type = 'text';
            searchInput.placeholder = 'Search sites...';
            searchClear.style.display = 'none';
            searchBox.appendChild(searchIcon);
            searchBox.appendChild(searchInput);
            searchBox.appendChild(searchClear);
            this.elements.menu.appendChild(searchBox);
            this.elements.searchInput = searchInput;
            this.elements.searchClear = searchClear;
            this.setupSearchEvents();
        }
        setupButtonEvents() {
            // Main button toggle
            this.elements.mainButton.addEventListener('click', (e) => {
                e.stopPropagation();
                this.toggleMenu();
            });
            // Quick access button
            this.elements.quickButton.addEventListener('click', (e) => {
                e.stopPropagation();
                this.favouritesManager.openAll();
            });
            // Close menu when clicking outside
            document.addEventListener('click', (event) => {
                if (this.menuVisible && !this.isMenuRelatedTarget(event.target)) {
                    this.closeMenu();
                }
            });
        }
        setupSearchEvents() {
            this.elements.searchInput.addEventListener('input', () => {
                const query = this.elements.searchInput.value.toLowerCase();
                this.elements.searchClear.style.display = query ? 'block' : 'none';
                this.filterSites(query);
            });
            this.elements.searchClear.addEventListener('click', () => {
                this.elements.searchInput.value = '';
                this.elements.searchClear.style.display = 'none';
                this.filterSites('');
                this.elements.searchInput.focus();
            });
            this.elements.searchInput.addEventListener('keypress', (e) => {
                if (e.key === 'Enter') {
                    e.preventDefault();
                    this.elements.searchInput.blur();
                }
            });
        }
        isMenuRelatedTarget(target) {
            return this.elements.menu.contains(target) ||
                   this.elements.mainButton.contains(target) ||
                   this.elements.quickButton.contains(target);
        }
        toggleMenu() {
            if (this.menuVisible) {
                this.closeMenu();
            } else {
                this.openMenu();
            }
        }
        openMenu() {
            this.menuVisible = true;
            this.elements.menu.style.display = 'block';
            this.elements.searchInput.focus();
            if (!this.elements.menu.hasBuiltContent) {
                this.buildMenuContent();
                this.elements.menu.hasBuiltContent = true;
            }
        }
        closeMenu() {
            this.menuVisible = false;
            this.elements.menu.style.display = 'none';
            this.elements.searchInput.value = '';
            this.elements.searchClear.style.display = 'none';
            this.closeAllFolders();
            // Restore page scrolling when menu closes
            document.body.style.overflowY = '';
            document.body.style.position = '';
            document.body.style.width = '';
        }
        closeAllFolders() {
            const folders = this.elements.menu.querySelectorAll('.ngsh-folder');
            folders.forEach(folder => {
                folder.classList.remove('open');
                const content = folder.querySelector('.ngsh-folder-content');
                if (content) content.style.maxHeight = '';
            });
        }
        filterSites(query) {
            if (!this.elements.menu.hasBuiltContent) return;
            // Close all folders first if search is cleared
            if (query === '') {
                this.closeAllFolders();
            }
            const folders = this.elements.menu.querySelectorAll('.ngsh-folder');
            let hasVisibleItems = false;
            folders.forEach(folder => {
                const content = folder.querySelector('.ngsh-folder-content');
                const items = content.querySelectorAll('.ngsh-menu-item:not(.ngsh-open-all)');
                let folderHasVisibleItems = false;
                items.forEach(item => {
                    const siteName = item.querySelector('.ngsh-site-name').textContent.toLowerCase();
                    const isVisible = query === '' || siteName.includes(query);
                    item.style.display = isVisible ? 'flex' : 'none';
                    if (isVisible) {
                        folderHasVisibleItems = true;
                        hasVisibleItems = true;
                    }
                });
                // Show/hide folder
                folder.style.display = (query === '' || folderHasVisibleItems) ? 'block' : 'none';
                // Keep Favourites folder visible
                const isFavouritesFolder = folder.querySelector('.ngsh-folder-title').textContent.includes('FAVOURITES');
                if (isFavouritesFolder) {
                    folder.style.display = 'block';
                }
                // Auto-open folders with search results
                if (query !== '' && folderHasVisibleItems) {
                    folder.classList.add('open');
                }
            });
            if (query !== '' && !hasVisibleItems) {
                NotificationManager.show('No sites match your search');
            }
        }
        buildMenuContent() {
            // Clear existing content
            this.elements.siteList.innerHTML = '';
            // Remove existing game info to prevent duplication
            const existingGameInfo = this.elements.menu.querySelector('.ngsh-game-info');
            if (existingGameInfo) existingGameInfo.remove();
            const { appId, gameName, developer } = GameInfoExtractor.getGameData();
            // Add Favourites folder
            const favouriteSites = this.favouritesManager.getAll();
            if (favouriteSites.length > 0) {
                const favouritesFolder = this.createFolder(
                    `FAVOURITES (${favouriteSites.length})`,
                    favouriteSites
                );
                this.elements.siteList.appendChild(favouritesFolder);
            }
            // Add category folders
            Object.entries(CONFIG.SITE_DEFINITIONS).forEach(([category, sites]) => {
                const folder = this.createFolder(category, sites);
                this.elements.siteList.appendChild(folder);
            });
            // Add game info
            if (gameName || appId || developer) {
                const gameInfo = this.createGameInfoSection(gameName, appId, developer);
                this.elements.menu.insertBefore(gameInfo, this.elements.menu.querySelector('.ngsh-search-box'));
            }
        }
        createFolder(title, sites) {
            const folder = this.createElement('div', 'ngsh-folder');
            // Header
            const header = this.createElement('div', 'ngsh-folder-header');
            const icon = this.createElement('div', 'ngsh-folder-icon', '▶');
            const titleElement = this.createElement('div', 'ngsh-folder-title', title);
            header.appendChild(icon);
            header.appendChild(titleElement);

            const content = this.createElement('div', 'ngsh-folder-content');

            // Sort sites: favourites first
            const sortedSites = [...sites].sort((a, b) => {
                const aFav = this.favouritesManager.isFavourite(a);
                const bFav = this.favouritesManager.isFavourite(b);
                if (aFav && !bFav) return -1;
                if (!aFav && bFav) return 1;
                return 0;
            });

            // Add "Open All" button for favourites folder
            if (title.includes('FAVOURITES')) {
                const openAllItem = this.createElement('div', 'ngsh-open-all');
                openAllItem.innerHTML = `<span class="ngsh-open-all-icon">💖</span><span>Open All Favourites</span>`;
                openAllItem.addEventListener('click', (e) => {
                    e.stopPropagation();
                    this.favouritesManager.openAll();
                });
                content.appendChild(openAllItem);
            }

            // Add site items
            sortedSites.forEach(site => {
                const item = this.createSiteItem(site);
                content.appendChild(item);
            });

            folder.appendChild(header);
            folder.appendChild(content);

            // Toggle functionality
            header.addEventListener('click', (e) => {
                e.stopPropagation();
                folder.classList.toggle('open');
                if (folder.classList.contains('open')) {
                    // Prevent page scrolling when folder is open - keep scrollbar visible
                    document.body.style.overflowY = 'scroll';
                    document.body.style.position = 'fixed';
                    document.body.style.width = '100%';
                } else {
                    // Allow page scrolling when folder closes
                    document.body.style.overflowY = '';
                    document.body.style.position = '';
                    document.body.style.width = '';
                }
            });

            return folder;
        }
        createSiteItem(site) {
            const item = this.createElement('div', 'ngsh-menu-item');
            const icon = this.createElement('div', 'ngsh-site-icon', site.icon || '🔍');
            const label = this.createElement('div', 'ngsh-site-name', site.name);
            const favourite = this.createElement('div', 'ngsh-favourite-icon');
            label.style.flex = '1';
            const isFav = this.favouritesManager.isFavourite(site);
            favourite.className = `ngsh-favourite-icon ${isFav ? 'favourited' : ''}`;
            favourite.textContent = isFav ? '❤' : '🖤';
            favourite.style.color = isFav ? CONFIG.colors.favourite : CONFIG.colors.nonFavourite;
            // Site click handler
            item.addEventListener('click', (e) => {
                e.stopPropagation();
                const { gameName, appId } = GameInfoExtractor.getGameData();
                SearchManager.performSearch(site, gameName, appId);
            });
            // Favourite toggle handler
            favourite.addEventListener('click', (e) => {
                e.stopPropagation();
                this.handleFavouriteToggle(site, favourite);
            });
            item.appendChild(icon);
            item.appendChild(label);
            item.appendChild(favourite);
            return item;
        }
        handleFavouriteToggle(site, favouriteIcon) {
            this.favouritesManager.toggle(site);
            const isFav = this.favouritesManager.isFavourite(site);
            favouriteIcon.className = `ngsh-favourite-icon ${isFav ? 'favourited' : ''}`;
            favouriteIcon.textContent = isFav ? '❤' : '🖤';
            favouriteIcon.style.color = isFav ? CONFIG.colors.favourite : CONFIG.colors.nonFavourite;
            // Update favourites folder count and content without rebuilding everything
            this.updateFavouritesFolder();
            // Re-sort the current folder to move favourites to top
            this.resortCurrentFolder(site);
            NotificationManager.show(isFav
                ? `Added to favourites: ${site.name}`
                : `Removed from favourites: ${site.name}`);
        }
        updateFavouritesFolder() {
            const favouriteSites = this.favouritesManager.getAll();
            const existingFavFolder = [...this.elements.siteList.children]
                .find(folder => folder.querySelector('.ngsh-folder-title').textContent.includes('FAVOURITES'));
            if (favouriteSites.length > 0) {
                if (existingFavFolder) {
                    // Completely rebuild the folder to avoid any leftover elements
                    existingFavFolder.remove();
                    const newFavouritesFolder = this.createFolder(
                        `FAVOURITES (${favouriteSites.length})`,
                        favouriteSites
                    );
                    this.elements.siteList.insertBefore(newFavouritesFolder, this.elements.siteList.firstChild);
                } else {
                    // Create new favourites folder and add it at the top
                    const favouritesFolder = this.createFolder(
                        `FAVOURITES (${favouriteSites.length})`,
                        favouriteSites
                    );
                    this.elements.siteList.insertBefore(favouritesFolder, this.elements.siteList.firstChild);
                }
            } else if (existingFavFolder) {
                // Remove favourites folder if no favourites
                existingFavFolder.remove();
            }

            // Update heart icons in all other folders
            this.updateAllHeartIcons();
        }

        updateAllHeartIcons() {
            // Update heart icons in all folders to reflect current favorite status
            const folders = this.elements.siteList.querySelectorAll('.ngsh-folder');
            folders.forEach(folder => {
                const folderTitle = folder.querySelector('.ngsh-folder-title').textContent;
                if (folderTitle.includes('FAVOURITES')) return; // Skip favourites folder

                const items = folder.querySelectorAll('.ngsh-menu-item:not(.ngsh-open-all)');
                items.forEach(item => {
                    const siteName = item.querySelector('.ngsh-site-name').textContent;
                    const site = this.findSiteByName(siteName);
                    const heartIcon = item.querySelector('.ngsh-favourite-icon');

                    if (site && heartIcon) {
                        const isFav = this.favouritesManager.isFavourite(site);
                        heartIcon.textContent = isFav ? '❤' : '🖤';
                        heartIcon.style.color = isFav ? CONFIG.colors.favourite : CONFIG.colors.nonFavourite;
                    }
                });
            });
        }

        resortCurrentFolder(site) {
            // Find the folder containing this site (not the favourites folder)
            const folders = this.elements.siteList.querySelectorAll('.ngsh-folder');
            folders.forEach(folder => {
                const folderTitle = folder.querySelector('.ngsh-folder-title').textContent;
                if (folderTitle.includes('FAVOURITES')) return; // Skip favourites folder
                // Check if this folder contains the site
                const items = folder.querySelectorAll('.ngsh-menu-item:not(.ngsh-open-all)');
                const siteItem = [...items].find(item =>
                    item.querySelector('.ngsh-site-name').textContent === site.name
                );
                if (siteItem) {
                    // Re-sort this folder's items
                    const content = folder.querySelector('.ngsh-folder-content');
                    const allItems = [...content.querySelectorAll('.ngsh-menu-item:not(.ngsh-open-all)')];
                    // Sort: favourites first
                    allItems.sort((a, b) => {
                        const aName = a.querySelector('.ngsh-site-name').textContent;
                        const bName = b.querySelector('.ngsh-site-name').textContent;
                        const aSite = this.findSiteByName(aName);
                        const bSite = this.findSiteByName(bName);
                        const aFav = aSite && this.favouritesManager.isFavourite(aSite);
                        const bFav = bSite && this.favouritesManager.isFavourite(bSite);
                        if (aFav && !bFav) return -1;
                        if (!aFav && bFav) return 1;
                        return 0;
                    });
                    // Remove all items and re-add in sorted order
                    allItems.forEach(item => item.remove());
                    allItems.forEach(item => content.appendChild(item));
                }
            });
        }
        findSiteByName(name) {
            const allSites = Object.values(CONFIG.SITE_DEFINITIONS).flat();
            return allSites.find(site => site.name === name);
        }
        createGameInfoSection(gameName, appId, developer) {
            const section = this.createElement('div', 'ngsh-game-info');
            if (gameName) {
                const nameEl = this.createElement('div', '', `${gameName}`);
                nameEl.style.marginBottom = '4px';
                section.appendChild(nameEl);
            }
            if (appId) {
                section.appendChild(this.createElement('div', '', `App ID: ${appId}`));
            }
            if (developer) {
                section.appendChild(this.createElement('div', '', `Developer: ${developer}`));
            }
            return section;
        }
        init() {
            if (document.querySelector('.ngsh-container')) return;
            this.createStyles();
            this.createButtons();
            this.createMenu();

            // Set up page scroll lock when hovering over menu - keep scrollbar visible
            this.elements.menu.addEventListener('mouseenter', () => {
                document.body.style.overflowY = 'scroll';
                document.body.style.position = 'fixed';
                document.body.style.width = '100%';
            });

            this.elements.menu.addEventListener('mouseleave', () => {
                document.body.style.overflowY = '';
                document.body.style.position = '';
                document.body.style.width = '';
            });
        }
    }
    // ======================
    // INITIALIZATION
    // ======================
    function init() {
        const ui = new UIManager();
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => ui.init());
        } else {
            ui.init();
        }
    }
    init();
})();