Discord Server Search

Search and navigate to a Discord server by name with real-time suggestions

// ==UserScript==
// @name         Discord Server Search
// @namespace    Made by @hakav
// @version      3.1
// @description  Search and navigate to a Discord server by name with real-time suggestions
// @match        https://discord.com/*
// @grant        none
// @icon         https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSrWjmWEq2JeU0yKb2ArlyAAJA4QQLkhbuihw&s
// @license      Copyright @hakav
// ==/UserScript==

(function() {
    'use strict';

    const recentSearches = [];
    let highlightedIndex = -1;

    // Softer color palette
    let primaryColor = '#1e1e2f'; // Light Blue
    let secondaryColor = '#6a0dad'; // Darker Blue
    let clearColor = '#4B0082'; // Dark Purple
    let clearHoverColor = '#6A0DAD'; // Lighter Purple

    const createSearchIcon = () => {
        const searchIcon = document.createElement('div');
        searchIcon.innerText = 'Search';
        searchIcon.style.position = 'fixed';
        searchIcon.style.bottom = '20px';
        searchIcon.style.right = '20px';
        searchIcon.style.background = `linear-gradient(135deg, ${secondaryColor}, ${primaryColor})`;
        searchIcon.style.color = 'white';
        searchIcon.style.borderRadius = '25px';
        searchIcon.style.padding = '10px 15px';
        searchIcon.style.cursor = 'pointer';
        searchIcon.style.zIndex = '1000';
        searchIcon.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.5)';
        searchIcon.style.transition = 'transform 0.3s, box-shadow 0.3s';
        document.body.appendChild(searchIcon);

        searchIcon.onmouseenter = () => {
            searchIcon.style.background = `rgba(169, 169, 169, 1)`; // Darker gray on hover
            searchIcon.style.boxShadow = '0 0 15px rgba(0, 0, 0, 0.5)';
            searchIcon.style.transform = 'scale(1.1) rotate(15deg)';
        };

        searchIcon.onmouseleave = () => {
            searchIcon.style.background = `rgba(100, 100, 100, 0.8)`; // Reset to gray background
            searchIcon.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.3)';
            searchIcon.style.transform = 'scale(1) rotate(0deg)';
        };

        const searchPopup = document.createElement('div');
        searchPopup.style.position = 'fixed';
        searchPopup.style.bottom = '80px';
        searchPopup.style.right = '-110px';
        searchPopup.style.background = `linear-gradient(135deg, ${primaryColor}, ${secondaryColor})`; // Gradient background
        searchPopup.style.padding = '15px';
        searchPopup.style.borderRadius = '10px';
        searchPopup.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.3)';
        searchPopup.style.zIndex = '1001';
        searchPopup.style.display = 'none';
        searchPopup.style.opacity = '0';
        searchPopup.style.transition = 'opacity 0.3s ease, right 0.5s ease, transform 0.5s ease';
        searchPopup.style.transform = 'translateY(20px)';
        document.body.appendChild(searchPopup);

        const searchInput = document.createElement('input');
        searchInput.placeholder = 'Search servers...';
        searchInput.style.width = '100%';
        searchInput.style.border = 'none';
        searchInput.style.borderRadius = '8px';
        searchInput.style.padding = '8px';
        searchInput.style.backgroundColor = 'rgba(255, 255, 255, 0.2)'; // Light transparent background
        searchInput.style.color = 'white';
        searchInput.style.fontSize = '14px';
        searchInput.style.outline = 'none';
        searchPopup.appendChild(searchInput);

        const typingAnimation = document.createElement('div');
        typingAnimation.style.position = 'absolute';
        typingAnimation.style.top = '50%';
        typingAnimation.style.left = '10px';
        typingAnimation.style.transform = 'translateY(-50%)';
        typingAnimation.style.color = secondaryColor;
        typingAnimation.style.fontSize = '14px';
        typingAnimation.style.opacity = '0';
        typingAnimation.style.zIndex = '1002';
        typingAnimation.innerText = '|';
        searchInput.appendChild(typingAnimation);

        const searchButton = document.createElement('button');
        searchButton.innerText = 'Search';
        searchButton.style.marginLeft = '5px';
        searchButton.style.borderRadius = '10px';
        searchButton.style.padding = '8px 12px';
        searchButton.style.background = `linear-gradient(135deg, ${primaryColor}, ${secondaryColor})`;
        searchButton.style.color = 'white';
        searchButton.style.border = 'none';
        searchButton.style.cursor = 'pointer';
        searchButton.style.fontSize = '14px';
        searchButton.style.transition = 'background 0.3s ease, transform 0.2s ease';
        searchButton.onclick = () => {
            searchButton.style.transform = 'scale(1.05)';
            setTimeout(() => searchButton.style.transform = 'scale(1)', 200);
        };
        searchPopup.appendChild(searchButton);

        const clearButton = document.createElement('button');
        clearButton.innerText = 'Clear';
        clearButton.style.marginLeft = '5px';
        clearButton.style.borderRadius = '10px';
        clearButton.style.padding = '8px 12px';
        clearButton.style.background = `linear-gradient(135deg, ${clearColor}, ${clearHoverColor})`;
        clearButton.style.color = 'white';
        clearButton.style.border = 'none';
        clearButton.style.cursor = 'pointer';
        clearButton.style.fontSize = '14px';
        clearButton.style.transition = 'background 0.3s ease, transform 0.2s ease';
        clearButton.onclick = () => {
            clearButton.style.transform = 'scale(1.05)';
            setTimeout(() => clearButton.style.transform = 'scale(1)', 200);
        };
        searchPopup.appendChild(clearButton);

        const loadingIndicator = document.createElement('div');
        loadingIndicator.style.display = 'none';
        loadingIndicator.innerText = 'Loading...';
        loadingIndicator.style.color = 'white';
        loadingIndicator.style.fontSize = '12px';
        searchPopup.appendChild(loadingIndicator);

        const suggestionBox = document.createElement('div');
        suggestionBox.style.position = 'absolute';
        suggestionBox.style.top = '50px';
        suggestionBox.style.left = '0';
        suggestionBox.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        suggestionBox.style.border = `1px solid ${secondaryColor}`;
        suggestionBox.style.width = '100%';
        suggestionBox.style.zIndex = '1003';
        suggestionBox.style.display = 'none';
        suggestionBox.style.maxHeight = '150px';
        suggestionBox.style.overflowY = 'auto';
        suggestionBox.style.borderRadius = '8px';
        suggestionBox.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.2)';
        searchPopup.appendChild(suggestionBox);

        const showPopup = () => {
            searchPopup.style.display = 'block';
            setTimeout(() => {
                searchPopup.style.opacity = '1';
                searchPopup.style.right = '20px';
                searchPopup.style.transform = 'translateY(0)';
            }, 10);
        };

        const hidePopup = () => {
            searchPopup.style.opacity = '0';
            searchPopup.style.right = '-110px';
            searchPopup.style.transform = 'translateY(20px)';
            setTimeout(() => {
                searchPopup.style.display = 'none';
                suggestionBox.style.display = 'none';
            }, 300);
        };

        searchIcon.onclick = () => {
            if (searchPopup.style.display === 'none' || searchPopup.style.opacity === '0') {
                showPopup();
                searchInput.focus();
            } else {
                hidePopup();
            }
        };

        window.onclick = (event) => {
            if (!searchIcon.contains(event.target) && !searchPopup.contains(event.target)) {
                hidePopup();
            }
        };

        const debounce = (func, delay) => {
            let timeout;
            return function(...args) {
                clearTimeout(timeout);
                timeout = setTimeout(() => func.apply(this, args), delay);
            };
        };

        const searchServers = debounce(() => {
            const query = searchInput.value.trim().toLowerCase();
            suggestionBox.innerHTML = '';

            if (query) {
                loadingIndicator.style.display = 'block';
                const servers = Array.from(document.querySelectorAll('[data-list-id="guildsnav"] [aria-label]'));
                const matchedServers = servers.filter(server =>
                    server.getAttribute('aria-label').toLowerCase().includes(query)
                );

                if (matchedServers.length > 0) {
                    matchedServers.forEach(server => {
                        const suggestionItem = document.createElement('div');
                        suggestionItem.textContent = server.getAttribute('aria-label');
                        suggestionItem.style.padding = '10px';
                        suggestionItem.style.cursor = 'pointer';
                        suggestionItem.style.color = 'white';
                        suggestionItem.style.fontSize = '14px';
                        suggestionItem.onclick = () => {
                            searchInput.value = server.getAttribute('aria-label');
                            suggestionBox.style.display = 'none';
                            server.click();
                        };
                        suggestionItem.onmouseenter = () => {
                            suggestionItem.style.backgroundColor = 'rgba(138, 43, 226, 0.3)';
                            suggestionItem.style.transition = 'background-color 0.2s ease';
                        };
                        suggestionItem.onmouseleave = () => suggestionItem.style.backgroundColor = 'transparent';
                        suggestionBox.appendChild(suggestionItem);
                    });
                    suggestionBox.style.display = 'block';
                } else {
                    suggestionBox.innerHTML = '<div style="color: white; padding: 8px;">No servers found.</div>';
                    suggestionBox.style.display = 'block';
                }
                loadingIndicator.style.display = 'none';
            } else {
                suggestionBox.style.display = 'none';
            }
        }, 300);

        searchInput.addEventListener('input', (e) => {
            searchServers();
            typingAnimation.style.opacity = '1';
            setTimeout(() => {
                typingAnimation.style.opacity = '0';
            }, 500);
        });

        searchInput.addEventListener('focus', () => {
            typingAnimation.style.display = 'block';
        });

        searchInput.addEventListener('blur', () => {
            typingAnimation.style.display = 'none';
        });

        searchButton.onclick = () => {
            const serverName = searchInput.value.trim();
            if (serverName) {
                recentSearches.push(serverName);
                const servers = Array.from(document.querySelectorAll('[data-list-id="guildsnav"] [aria-label]'));
                const matchedServer = servers.find(server =>
                    server.getAttribute('aria-label').toLowerCase() === serverName.toLowerCase()
                );

                if (matchedServer) {
                    matchedServer.click();
                } else {
                    console.log('Server not found!');
                }
            }
        };

        clearButton.onclick = () => {
            searchInput.value = '';
            suggestionBox.style.display = 'none';
        };

        searchInput.addEventListener('keydown', (e) => {
            const suggestionItems = Array.from(suggestionBox.children);
            if (e.key === 'ArrowDown') {
                highlightedIndex = Math.min(highlightedIndex + 1, suggestionItems.length - 1);
            } else if (e.key === 'ArrowUp') {
                highlightedIndex = Math.max(highlightedIndex - 1, 0);
            }

            suggestionItems.forEach((item, index) => {
                item.style.backgroundColor = index === highlightedIndex ? 'rgba(138, 43, 226, 0.3)' : 'transparent';
                item.style.transition = 'background-color 0.2s ease';
            });

            if (e.key === 'Enter' && highlightedIndex >= 0) {
                suggestionItems[highlightedIndex].click();
            }
        });
    };

    // Wait for the window to fully load before creating the icon
    window.onload = createSearchIcon;

})();