Discord Server Search

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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;

})();