Discord Server Search

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

目前為 2024-10-12 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Discord Server Search
// @namespace    Made by @hakav
// @version      3.0.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;

    const createSearchIcon = () => {
        const searchIcon = document.createElement('div');
        searchIcon.innerHTML = '🔍';
        searchIcon.style.position = 'fixed';
        searchIcon.style.bottom = '20px';
        searchIcon.style.right = '20px';
        searchIcon.style.backgroundColor = 'rgba(128, 0, 128, 0.3)'; // Purple background
        searchIcon.style.color = 'white';
        searchIcon.style.border = '2px solid rgba(255, 255, 255, 0.3)';
        searchIcon.style.borderRadius = '50%';
        searchIcon.style.padding = '10px';
        searchIcon.style.cursor = 'pointer';
        searchIcon.style.zIndex = '1000';
        searchIcon.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.2)';
        searchIcon.style.transition = 'background-color 0.3s ease, box-shadow 0.3s ease, transform 0.2s ease';
        document.body.appendChild(searchIcon);

        searchIcon.onmouseenter = () => {
            searchIcon.style.backgroundColor = 'rgba(128, 0, 128, 0.6)';
            searchIcon.style.boxShadow = '0 0 15px rgba(0, 0, 0, 0.5)';
            searchIcon.style.transform = 'scale(1.1)';
        };

        searchIcon.onmouseleave = () => {
            searchIcon.style.backgroundColor = 'rgba(128, 0, 128, 0.3)';
            searchIcon.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.2)';
            searchIcon.style.transform = 'scale(1)';
        };

        const searchPopup = document.createElement('div');
        searchPopup.style.position = 'fixed';
        searchPopup.style.bottom = '80px';
        searchPopup.style.right = '-220px'; // Start hidden, reduced width
        searchPopup.style.backgroundColor = 'black'; // Black background
        searchPopup.style.padding = '10px'; // Reduced padding
        searchPopup.style.borderRadius = '15px';
        searchPopup.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.3)';
        searchPopup.style.zIndex = '1001';
        searchPopup.style.display = 'none'; // Initially hidden
        searchPopup.style.opacity = '0';
        searchPopup.style.transition = 'opacity 0.3s ease, right 0.5s ease';
        document.body.appendChild(searchPopup);

        const searchInput = document.createElement('input');
        searchInput.placeholder = 'Server name...'; // Shortened placeholder
        searchInput.style.width = '100%'; // Full width
        searchInput.style.border = '2px solid purple'; // Purple border
        searchInput.style.borderRadius = '5px'; // Less rounded corners
        searchInput.style.padding = '6px'; // Reduced padding
        searchInput.style.backgroundColor = 'rgba(128, 0, 128, 0.1)'; // Light purple background
        searchInput.style.color = 'white'; // White text
        searchInput.style.fontSize = '14px'; // Smaller font size
        searchPopup.appendChild(searchInput);

        const searchButton = document.createElement('button');
        searchButton.innerText = 'Search';
        searchButton.style.marginLeft = '5px'; // Reduced margin
        searchButton.style.borderRadius = '5px'; // Less rounded corners
        searchButton.style.padding = '6px 10px'; // Reduced padding
        searchButton.style.backgroundColor = 'purple'; // Purple background
        searchButton.style.color = 'white';
        searchButton.style.border = 'none';
        searchButton.style.cursor = 'pointer';
        searchButton.style.fontSize = '14px'; // Smaller font size
        searchPopup.appendChild(searchButton);

        const clearButton = document.createElement('button');
        clearButton.innerText = 'Clear';
        clearButton.style.marginLeft = '5px';
        clearButton.style.borderRadius = '5px';
        clearButton.style.padding = '6px 10px';
        clearButton.style.backgroundColor = 'red'; // Red background for clear
        clearButton.style.color = 'white';
        clearButton.style.border = 'none';
        clearButton.style.cursor = 'pointer';
        clearButton.style.fontSize = '14px';
        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 = '40px'; // Adjusted for better positioning
        suggestionBox.style.left = '0';
        suggestionBox.style.backgroundColor = 'black'; // Black background
        suggestionBox.style.border = '1px solid purple'; // Purple border
        suggestionBox.style.width = '100%'; // Full width
        suggestionBox.style.zIndex = '1002';
        suggestionBox.style.display = 'none';
        suggestionBox.style.maxHeight = '120px'; // Reduced max height
        suggestionBox.style.overflowY = 'auto'; // Scroll for longer lists
        suggestionBox.style.borderRadius = '5px'; // Less rounded corners
        suggestionBox.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.2)'; // Soft shadow
        searchPopup.appendChild(suggestionBox);

        const showPopup = () => {
            searchPopup.style.display = 'block';
            setTimeout(() => {
                searchPopup.style.opacity = '1';
                searchPopup.style.right = '20px'; // Slide it into view
            }, 10);
        };

        const hidePopup = () => {
            searchPopup.style.opacity = '0';
            searchPopup.style.right = '-220px'; // Slide it back out
            setTimeout(() => {
                searchPopup.style.display = 'none';
                suggestionBox.style.display = 'none'; // Hide suggestion box
            }, 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 = ''; // Clear previous suggestions

            if (query) {
                loadingIndicator.style.display = 'block'; // Show loading
                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 = '5px';
                        suggestionItem.style.cursor = 'pointer';
                        suggestionItem.style.color = 'white'; // White text for suggestions
                        suggestionItem.style.fontSize = '14px'; // Smaller font size
                        suggestionItem.onclick = () => {
                            searchInput.value = server.getAttribute('aria-label');
                            suggestionBox.style.display = 'none';
                            server.click(); // Click the server to open it
                        };
                        suggestionItem.onmouseenter = () => suggestionItem.style.backgroundColor = 'rgba(128, 0, 128, 0.3)'; // Highlight on hover
                        suggestionItem.onmouseleave = () => suggestionItem.style.backgroundColor = 'black'; // Reset background
                        suggestionBox.appendChild(suggestionItem);
                    });
                    suggestionBox.style.display = 'block';
                } else {
                    suggestionBox.innerHTML = '<div style="color: white; padding: 5px;">No servers found.</div>';
                    suggestionBox.style.display = 'block';
                }
                loadingIndicator.style.display = 'none'; // Hide loading
            } else {
                suggestionBox.style.display = 'none';
            }
        }, 300); // Debounce delay of 300ms

        searchInput.addEventListener('input', searchServers);

        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(); // Click the server to navigate
                } 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(128, 0, 128, 0.3)' : 'black';
            });

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

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

})();