Floating Link Menu

Customizable link menu.

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

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

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

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

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

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

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Floating Link Menu
// @namespace    http://tampermonkey.net/
// @version      2.7 universal
// @description  Customizable link menu.
// @author       echoZ
// @license      MIT
// @match        *://*/*
// @exclude      *://*routerlogin.net/*
// @exclude      *://*192.168.1.1/*
// @exclude      *://*192.168.0.1/*
// @exclude      *://*my.bankofamerica.com/*
// @exclude      *://*wellsfargo.com/*
// @exclude      *://*chase.com/*
// @exclude      *://*citibank.com/*
// @exclude      *://*online.citi.com/*
// @exclude      *://*capitalone.com/*
// @exclude      *://*usbank.com/*
// @exclude      *://*paypal.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(async function() {
    'use strict';

    // --- SCRIPT EXCLUSION LOGIC ---
    const excludedDomainsStorageKey = 'excludedUniversalDomains';
    const currentUrl = window.location.href;
    const excludedDomains = await GM_getValue(excludedDomainsStorageKey, []);
    const isExcluded = excludedDomains.some(domain => currentUrl.includes(domain));
    if (isExcluded) {
        return;
    }
    // --- END EXCLUSION LOGIC ---

    // --- Data Management using Tampermonkey's GM_ API ---
    const storageKey = 'universalLinkManagerLinks';
    const isBubbleHiddenStorageKey = 'isBubbleHidden';
    const buttonPositionStorageKey = 'bubblePosition';
    const themeStorageKey = 'universalLinkManagerTheme';

    const defaultLinks = [
        { label: 'Google', url: 'https://www.google.com/' },
        { label: 'Gemini AI', url: 'https://gemini.google.com/' },
        { label: 'OpenAI', url: 'https://www.openai.com/' }
    ];

    let isDeleteMode = false;
    let isExcludeDeleteMode = false;

    async function getLinks() {
        return await GM_getValue(storageKey, defaultLinks);
    }

    async function saveLinks(links) {
        await GM_setValue(storageKey, links);
    }

    async function getExcludedDomains() {
        return await GM_getValue(excludedDomainsStorageKey, []);
    }

    async function saveExcludedDomains(domains) {
        await GM_setValue(excludedDomainsStorageKey, domains);
    }

    async function getBubbleHiddenState() {
        return await GM_getValue(isBubbleHiddenStorageKey, false);
    }

    async function saveBubbleHiddenState(isHidden) {
        await GM_setValue(isBubbleHiddenStorageKey, isHidden);
    }

    async function getButtonPosition() {
        return await GM_getValue(buttonPositionStorageKey, { vertical: 'bottom', horizontal: 'right' });
    }

    async function saveButtonPosition(position) {
        await GM_setValue(buttonPositionStorageKey, position);
    }

    async function getTheme() {
        return await GM_getValue(themeStorageKey, 'default');
    }

    async function saveTheme(theme) {
        await GM_setValue(themeStorageKey, theme);
    }

    // --- Style and Themes ---
    const themes = {
        default: `
            #floatingMenu, #universalLinkManagerUI, #bubbleMenu { background-color: #222; border: 2px solid #0ff; box-shadow: 0 0 10px rgba(0,255,255,0.7); }
            #linkList a, .exclude-wrapper span { color: #fff; background-color: #333; border: 1px solid #0ff; }
            #linkList a:hover { background-color: #0ff; color: #000; }
            #menuControls button, #backupSection button, #positionControls button, .modal-button, #bubbleMenu button { color: #0ff; border: 1px solid #0ff; background-color: #444; }
            #menuControls button:hover, #backupSection button:hover, #positionControls button:hover, .modal-button:hover, #bubbleMenu button:hover { background-color: #0ff; color: #000; }
            #linkForm input, #excludeSection input { border: 1px solid #0ff; background-color: #333; color: #fff; }
            #linkForm h3, #excludeSection h3, #backupSection h3, #positionControls h3 { color: #fff; }
            .delete-link-button, .delete-exclude-button { background-color: #a00; border: 1px solid #f00; color: #fff; }
            .delete-link-button:hover, .delete-exclude-button:hover { background-color: #f00; }
            .active { background-color: #0ff; color: #000 !important; }
        `,
        highContrast: `
            #floatingMenu, #universalLinkManagerUI, #bubbleMenu { background-color: #000; border: 2px solid #ffff00; box-shadow: 0 0 15px rgba(255,255,0,0.9); }
            #linkList a, .exclude-wrapper span { color: #ffff00; background-color: #000; border: 1px solid #ffff00; text-shadow: 0 0 5px #ffff00; font-weight: bold; }
            #linkList a:hover { background-color: #ffff00; color: #000; text-shadow: none; }
            #menuControls button, #backupSection button, #positionControls button, .modal-button, #bubbleMenu button { color: #ffff00; border: 1px solid #ffff00; background-color: #333; }
            #menuControls button:hover, #backupSection button:hover, #positionControls button:hover, .modal-button:hover, #bubbleMenu button:hover { background-color: #ffff00; color: #000; }
            #linkForm input, #excludeSection input { border: 1px solid #ffff00; background-color: #000; color: #ffff00; }
            #linkForm h3, #excludeSection h3, #backupSection h3, #positionControls h3 { color: #ffff00; }
            .delete-link-button, .delete-exclude-button { background-color: #ff0000; border: 1px solid #ff0000; color: #ffff00; }
            .delete-link-button:hover, .delete-exclude-button:hover { background-color: #ff5555; }
            .active { background-color: #ffff00; color: #000 !important; }
        `
    };

    const baseStyle = `
        @keyframes pulse {
            0% { transform: scale(1); box-shadow: 0 0 15px 3px #0ff, 0 0 30px 10px #0ff; }
            50% { transform: scale(1.05); box-shadow: 0 0 20px 5px #0ff, 0 0 40px 15px #0ff; }
            100% { transform: scale(1); box-shadow: 0 0 15px 3px #0ff, 0 0 30px 10px #0ff; }
        }
        @keyframes neonGlow {
            0% { box-shadow: 0 0 10px rgba(0,255,255,0.7); }
            50% { box-shadow: 0 0 15px rgba(0,255,255,0.9), 0 0 25px rgba(0,255,255,0.6); }
            100% { box-shadow: 0 0 10px rgba(0,255,255,0.7); }
        }

        #customFloatingBubble {
            position: fixed;
            width: 60px;
            height: 60px;
            background-color: #0ff;
            border-radius: 50%;
            box-shadow: 0 0 15px 3px #0ff, 0 0 30px 10px #0ff;
            cursor: pointer;
            z-index: 9999999;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 36px;
            font-weight: 900;
            color: #001f3f;
            user-select: none;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            transition: transform 0.2s ease, box-shadow 0.2s ease, top 0.2s ease, bottom 0.2s ease, left 0.2s ease, right 0.2s ease;
            animation: pulse 3s infinite ease-in-out;
        }
        #customFloatingBubble:hover {
            transform: scale(1.15);
            box-shadow: 0 0 20px 5px #0ff, 0 0 40px 15px #0ff;
        }
        #universalLinkManagerUI {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 350px;
            border-radius: 8px;
            padding: 15px;
            z-index: 9999998;
            display: none;
            flex-direction: column;
            gap: 15px;
            max-height: 90vh;
            overflow-y: auto;
            animation: neonGlow 4s infinite ease-in-out;
        }
        #bubbleMenu {
            position: fixed;
            z-index: 9999999;
            padding: 10px;
            border-radius: 8px;
            display: none;
            flex-direction: column;
            gap: 8px;
            animation: neonGlow 4s infinite ease-in-out;
        }
        #bubbleMenu button {
            transition: background-color 0.2s, color 0.2s;
        }
        #ui-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 1px solid #555;
            padding-bottom: 10px;
        }
        #ui-header h2 {
            margin: 0;
            color: #0ff;
            text-shadow: 0 0 5px #0ff;
        }
        #closeUI {
            background: none;
            border: none;
            color: #fff;
            font-size: 24px;
            cursor: pointer;
            padding: 0;
            width: 30px;
            height: 30px;
            border-radius: 50%;
            transition: all 0.2s ease;
        }
        #closeUI:hover {
            background-color: #f00;
            transform: scale(1.1);
        }
        #linkList, #excludeList {
            display: flex;
            flex-direction: column;
            gap: 5px;
        }
        .link-wrapper, .exclude-wrapper {
            display: flex;
            align-items: center;
            gap: 5px;
        }
        #linkList a, .exclude-wrapper span {
            flex-grow: 1;
            padding: 8px;
            text-align: center;
            text-decoration: none;
            border-radius: 5px;
            transition: background-color 0.2s ease, color 0.2s ease;
            text-shadow: 0 0 3px currentColor;
            font-size: 14px;
        }
        .delete-link-button, .delete-exclude-button {
            width: 30px;
            height: 30px;
            border-radius: 50%;
            cursor: pointer;
            font-weight: bold;
            transition: background-color 0.2s ease;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 0;
        }
        #menuControls {
            display: flex;
            flex-wrap: wrap;
            justify-content: space-between;
            gap: 5px;
        }
        #menuControls button, .modal-button {
            padding: 8px 12px;
            border-radius: 5px;
            cursor: pointer;
            flex: 1 1 45%;
            font-size: 12px;
            text-align: center;
        }
        #linkForm, #excludeSection, #backupSection, #positionControls {
            display: flex;
            flex-direction: column;
            gap: 5px;
            padding-top: 10px;
            border-top: 1px solid #444;
        }
        #linkForm h3, #excludeSection h3, #backupSection h3, #positionControls h3 {
            margin: 0;
            text-align: center;
        }
        #linkForm input, #excludeSection input {
            padding: 8px;
            border-radius: 5px;
        }
        #backupSection button {
            flex: 1;
            font-size: 14px;
        }
        #showBubbleButton {
            position: fixed;
            width: 60px;
            height: 60px;
            cursor: pointer;
            z-index: 9999997;
            background-color: transparent;
            border: none;
            user-select: none;
        }
        #positionControls .position-buttons {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 5px;
        }
        .import-buttons {
            display: flex;
            gap: 5px;
        }
        .import-buttons button {
            flex: 1;
        }
    `;

    GM_addStyle(baseStyle);

    let bubble = null;
    let mainUI = null;
    let showBubbleButton = null;
    let bubbleMenu = null;

    function populateLinkList(links, linkListElement) {
        linkListElement.innerHTML = '';
        links.forEach((linkData, index) => {
            const linkWrapper = document.createElement('div');
            linkWrapper.className = 'link-wrapper';

            const link = document.createElement('a');
            link.href = linkData.url;
            link.textContent = linkData.label;
            link.target = '_blank';

            linkWrapper.appendChild(link);

            if (isDeleteMode) {
                const deleteButton = document.createElement('button');
                deleteButton.className = 'delete-link-button';
                deleteButton.textContent = 'x';
                deleteButton.addEventListener('click', async (event) => {
                    event.preventDefault();
                    links.splice(index, 1);
                    await saveLinks(links);
                    populateLinkList(links, linkListElement);
                });
                linkWrapper.appendChild(deleteButton);
            }
            linkListElement.appendChild(linkWrapper);
        });
    }

    function populateExcludeList(domains, excludeListElement) {
        excludeListElement.innerHTML = '';
        domains.forEach((domain, index) => {
            const domainWrapper = document.createElement('div');
            domainWrapper.className = 'exclude-wrapper';

            const domainLabel = document.createElement('span');
            domainLabel.textContent = domain;
            domainWrapper.appendChild(domainLabel);

            if (isExcludeDeleteMode) {
                const deleteButton = document.createElement('button');
                deleteButton.className = 'delete-exclude-button';
                deleteButton.textContent = 'x';
                deleteButton.addEventListener('click', async (event) => {
                    event.preventDefault();
                    domains.splice(index, 1);
                    await saveExcludedDomains(domains);
                    populateExcludeList(domains, excludeListElement);
                });
                domainWrapper.appendChild(deleteButton);
            }
            excludeListElement.appendChild(domainWrapper);
        });
    }

    function applyButtonPosition(position) {
        if (bubble) {
            bubble.style.top = '';
            bubble.style.left = '';
            bubble.style.bottom = '';
            bubble.style.right = '';
            bubble.style[position.vertical] = '30px';
            bubble.style[position.horizontal] = '30px';
        }
        if (showBubbleButton) {
            showBubbleButton.style.top = '';
            showBubbleButton.style.left = '';
            showBubbleButton.style.bottom = '';
            showBubbleButton.style.right = '';
            showBubbleButton.style[position.vertical] = '30px';
            showBubbleButton.style[position.horizontal] = '30px';
        }
        if (bubbleMenu) {
            bubbleMenu.style.top = '';
            bubbleMenu.style.left = '';
            bubbleMenu.style.bottom = '';
            bubbleMenu.style.right = '';

            if (position.vertical === 'top') {
                bubbleMenu.style.top = '100px';
            } else {
                bubbleMenu.style.bottom = '100px';
            }

            if (position.horizontal === 'left') {
                bubbleMenu.style.left = '30px';
            } else {
                bubbleMenu.style.right = '30px';
            }
        }

        const positionButtons = document.querySelectorAll('#positionControls button');
        positionButtons.forEach(btn => {
            btn.classList.remove('active');
        });
        const activeBtnId = `position-${position.vertical}-${position.horizontal}`;
        const activeBtn = document.getElementById(activeBtnId);
        if (activeBtn) {
            activeBtn.classList.add('active');
        }
    }

    function applyTheme(themeName) {
        const styleElement = document.getElementById('universalLinkManagerStyle');
        if (styleElement) {
            styleElement.innerHTML = `${baseStyle}\n${themes[themeName]}\n`;
        }
    }

    async function initializeScript() {
        if (document.getElementById('customFloatingBubble')) {
            return;
        }

        const buttonPosition = await getButtonPosition();
        const currentTheme = await getTheme();
        
        const style = document.createElement('style');
        style.id = 'universalLinkManagerStyle';
        document.head.appendChild(style);
        applyTheme(currentTheme);

        // Create the bubble
        bubble = document.createElement('div');
        bubble.id = 'customFloatingBubble';
        bubble.textContent = 'λ';
        document.body.appendChild(bubble);

        // Create the mini bubble menu
        bubbleMenu = document.createElement('div');
        bubbleMenu.id = 'bubbleMenu';
        bubbleMenu.innerHTML = `
            <button id="instantAddButton">Add This Site</button>
            <button id="showFullMenuButton">Settings</button>
        `;
        document.body.appendChild(bubbleMenu);

        // Create the main modal UI
        mainUI = document.createElement('div');
        mainUI.id = 'universalLinkManagerUI';
        mainUI.innerHTML = `
            <div id="ui-header">
                <h2>Universal Links</h2>
                <button id="closeUI">X</button>
            </div>
            <div id="ui-content">
                <div id="linkList"></div>
                <div id="linkForm">
                    <h3>Add New Link</h3>
                    <input type="text" id="linkLabel" placeholder="Label (e.g. My Site)">
                    <input type="text" id="linkUrl" placeholder="URL (e.g. https://example.com)">
                    <button id="saveLinkButton" class="modal-button">Save</button>
                </div>
                <div id="excludeSection">
                    <h3>Excluded Websites</h3>
                    <div id="excludeList"></div>
                    <input type="text" id="excludeUrl" placeholder="Domain (e.g. example.com)">
                    <button id="saveExcludeButton" class="modal-button">Add Exclude</button>
                    <button id="deleteExcludeButton" class="modal-button">Delete Excludes</button>
                </div>
                <div id="backupSection">
                    <h3>Backup & Restore</h3>
                    <div id="exportWrapper">
                        <button id="exportButton" class="modal-button">Export</button>
                    </div>
                    <div id="importWrapper">
                        <button id="importButton" class="modal-button">Import</button>
                    </div>
                </div>
                <div id="positionControls">
                    <h3>Button Position</h3>
                    <div class="position-buttons">
                        <button id="position-top-left" class="modal-button">Top-Left</button>
                        <button id="position-top-right" class="modal-button">Top-Right</button>
                        <button id="position-bottom-left" class="modal-button">Bottom-Left</button>
                        <button id="position-bottom-right" class="modal-button">Bottom-Right</button>
                    </div>
                </div>
                <div id="menuControls">
                    <button id="deleteLinksButton" class="modal-button">Delete Links</button>
                    <button id="themeButton" class="modal-button">Theme</button>
                    <button id="hideButton" class="modal-button">Hide Button</button>
                </div>
            </div>
        `;
        document.body.appendChild(mainUI);
        
        // Create the invisible show bubble button
        showBubbleButton = document.createElement('div');
        showBubbleButton.id = 'showBubbleButton';
        document.body.appendChild(showBubbleButton);

        // Load initial state and position
        applyButtonPosition(buttonPosition);
        const isHidden = await getBubbleHiddenState();
        bubble.style.display = isHidden ? 'none' : 'flex';
        showBubbleButton.style.display = isHidden ? 'block' : 'none';

        // --- Event Listeners ---

        // NEW: Keyboard shortcut to toggle the main UI
        document.addEventListener('keydown', async (event) => {
            // Check for Ctrl + Alt + U
            if (event.ctrlKey && event.altKey && event.key === 'u') {
                const isVisible = mainUI.style.display === 'flex';
                if (isVisible) {
                    mainUI.style.display = 'none';
                } else {
                    const links = await getLinks();
                    const excluded = await getExcludedDomains();
                    populateLinkList(links, mainUI.querySelector('#linkList'));
                    populateExcludeList(excluded, mainUI.querySelector('#excludeList'));
                    mainUI.style.display = 'flex';
                }
                event.preventDefault(); // Prevents the browser's default action
            }
        });

        // Bubble click to toggle the mini-menu
        bubble.addEventListener('click', async () => {
            const isVisible = bubbleMenu.style.display === 'flex';
            bubbleMenu.style.display = isVisible ? 'none' : 'flex';
        });

        // Triple-click logic for bubble
        let bubbleClickCount = 0;
        let bubbleClickTimer = null;
        bubble.addEventListener('click', () => {
            bubbleClickCount++;
            if (bubbleClickTimer) clearTimeout(bubbleClickTimer);
            bubbleClickTimer = setTimeout(() => {
                bubbleClickCount = 0;
            }, 300);

            if (bubbleClickCount === 3) {
                clearTimeout(bubbleClickTimer);
                bubble.style.display = 'none';
                showBubbleButton.style.display = 'block';
                mainUI.style.display = 'none';
                bubbleMenu.style.display = 'none';
                saveBubbleHiddenState(true);
                bubbleClickCount = 0;
            }
        });

        // Triple-click logic for showBubbleButton
        let restoreClickCount = 0;
        let restoreClickTimer = null;
        showBubbleButton.addEventListener('click', () => {
            restoreClickCount++;
            if (restoreClickTimer) clearTimeout(restoreClickTimer);
            restoreClickTimer = setTimeout(() => {
                restoreClickCount = 0;
            }, 400);

            if (restoreClickCount === 3) {
                clearTimeout(restoreClickTimer);
                bubble.style.display = 'flex';
                showBubbleButton.style.display = 'none';
                saveBubbleHiddenState(false);
                restoreClickCount = 0;
            }
        });
        
        // Instant Add Button
        bubbleMenu.querySelector('#instantAddButton').addEventListener('click', async () => {
            const title = document.title;
            const url = window.location.href;
            const links = await getLinks();
            
            links.push({ label: title, url: url });
            await saveLinks(links);
            
            bubbleMenu.style.display = 'none';
            const excluded = await getExcludedDomains();
            populateLinkList(links, mainUI.querySelector('#linkList'));
            populateExcludeList(excluded, mainUI.querySelector('#excludeList'));
            mainUI.style.display = 'flex';
        });

        // Show Full Menu Button
        bubbleMenu.querySelector('#showFullMenuButton').addEventListener('click', async () => {
            bubbleMenu.style.display = 'none';
            const links = await getLinks();
            const excluded = await getExcludedDomains();
            populateLinkList(links, mainUI.querySelector('#linkList'));
            populateExcludeList(excluded, mainUI.querySelector('#excludeList'));
            mainUI.style.display = 'flex';
        });

        mainUI.querySelector('#closeUI').addEventListener('click', () => {
            mainUI.style.display = 'none';
        });

        mainUI.querySelector('#hideButton').addEventListener('click', () => {
            bubble.style.display = 'none';
            showBubbleButton.style.display = 'block';
            mainUI.style.display = 'none';
            bubbleMenu.style.display = 'none';
            saveBubbleHiddenState(true);
        });

        mainUI.querySelector('#saveLinkButton').addEventListener('click', async () => {
            const labelInput = mainUI.querySelector('#linkLabel');
            const urlInput = mainUI.querySelector('#linkUrl');
            const label = labelInput.value.trim();
            const url = urlInput.value.trim();
            if (label && url) {
                try {
                    new URL(url);
                    const links = await getLinks();
                    links.push({ label, url });
                    await saveLinks(links);
                    populateLinkList(links, mainUI.querySelector('#linkList'));
                    labelInput.value = '';
                    urlInput.value = '';
                } catch (e) {
                    alert('Please enter a valid URL (e.g., https://example.com).');
                }
            } else {
                alert('Please enter both a label and a URL.');
            }
        });

        mainUI.querySelector('#deleteLinksButton').addEventListener('click', async () => {
            isDeleteMode = !isDeleteMode;
            const deleteButton = mainUI.querySelector('#deleteLinksButton');
            deleteButton.textContent = isDeleteMode ? 'Exit Delete' : 'Delete Links';
            const links = await getLinks();
            populateLinkList(links, mainUI.querySelector('#linkList'));
        });

        mainUI.querySelector('#themeButton').addEventListener('click', async () => {
            const currentTheme = await getTheme();
            const newTheme = currentTheme === 'default' ? 'highContrast' : 'default';
            await saveTheme(newTheme);
            applyTheme(newTheme);
            const themeButton = mainUI.querySelector('#themeButton');
            themeButton.textContent = newTheme === 'default' ? 'Theme' : 'Default';
        });

        mainUI.querySelector('#saveExcludeButton').addEventListener('click', async () => {
            const domainInput = mainUI.querySelector('#excludeUrl');
            const domain = domainInput.value.trim();
            if (domain) {
                const excluded = await getExcludedDomains();
                if (!excluded.includes(domain)) {
                    excluded.push(domain);
                    await saveExcludedDomains(excluded);
                    populateExcludeList(excluded, mainUI.querySelector('#excludeList'));
                    domainInput.value = '';
                } else {
                    alert('This domain is already on the exclusion list.');
                }
            } else {
                alert('Please enter a domain to exclude.');
            }
        });

        mainUI.querySelector('#deleteExcludeButton').addEventListener('click', async () => {
            isExcludeDeleteMode = !isExcludeDeleteMode;
            const deleteExcludeButton = mainUI.querySelector('#deleteExcludeButton');
            deleteExcludeButton.textContent = isExcludeDeleteMode ? 'Exit Exclude Delete' : 'Delete Excludes';
            const excluded = await getExcludedDomains();
            populateExcludeList(excluded, mainUI.querySelector('#excludeList'));
        });

        mainUI.querySelector('#positionControls').querySelectorAll('button').forEach(button => {
            button.addEventListener('click', async (event) => {
                const id = event.target.id;
                const parts = id.split('-');
                const newPosition = { vertical: parts[1], horizontal: parts[2] };
                await saveButtonPosition(newPosition);
                applyButtonPosition(newPosition);
            });
        });

        mainUI.querySelector('#exportWrapper').addEventListener('click', async (event) => {
            const button = event.target;
            if (button.id === 'exportButton') {
                if (button.textContent === 'Export') {
                    const links = await getLinks();
                    const exportData = JSON.stringify(links, null, 2);
                    mainUI.querySelector('#exportWrapper').innerHTML = `
                        <textarea readonly style="width:100%; height:100px;">${exportData}</textarea>
                        <button id="exportButton" class="modal-button">Close</button>
                    `;
                } else {
                    mainUI.querySelector('#exportWrapper').innerHTML = `<button id="exportButton" class="modal-button">Export</button>`;
                }
            }
        });

        mainUI.querySelector('#importWrapper').addEventListener('click', async (event) => {
            const button = event.target;
            if (button.id === 'importButton') {
                if (button.textContent === 'Import') {
                    mainUI.querySelector('#importWrapper').innerHTML = `
                        <textarea placeholder="Paste your link data here..." style="width:100%; height:100px;"></textarea>
                        <div class="import-buttons">
                            <button id="loadButton" class="modal-button">Load</button>
                            <button id="importButton" class="modal-button">Cancel</button>
                        </div>
                    `;
                } else {
                    mainUI.querySelector('#importWrapper').innerHTML = `<button id="importButton" class="modal-button">Import</button>`;
                }
            } else if (button.id === 'loadButton') {
                const dataTextarea = mainUI.querySelector('textarea');
                if (dataTextarea) {
                    const data = dataTextarea.value.trim();
                    if (!data) {
                        alert('Please paste the link data.');
                        return;
                    }
                    try {
                        const importedLinks = JSON.parse(data);
                        if (Array.isArray(importedLinks)) {
                            await saveLinks(importedLinks);
                            const links = await getLinks();
                            populateLinkList(links, mainUI.querySelector('#linkList'));
                            alert('Links imported successfully!');
                            mainUI.querySelector('#importWrapper').innerHTML = `<button id="importButton" class="modal-button">Import</button>`;
                        } else {
                            alert('Invalid data format. Please paste the correct JSON data.');
                        }
                    } catch (e) {
                        alert('Invalid JSON format. Error: ' + e.message);
                    }
                }
            }
        });
    }

    if (document.body) {
        initializeScript();
    } else {
        document.addEventListener('DOMContentLoaded', initializeScript);
    }
})();