Discord Server Search

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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;

})();