多平台右侧导航面板增强版

在右侧显示知乎、B站、豆瓣、X(推特)、Pixiv、微博和Reddit的独立导航面板

// ==UserScript==
// @name         多平台右侧导航面板增强版
// @namespace    http://tampermonkey.net/
// @version      5.3
// @description  在右侧显示知乎、B站、豆瓣、X(推特)、Pixiv、微博和Reddit的独立导航面板
// @author       You
// @match        https://*.zhihu.com/*
// @match        https://*.bilibili.com/*
// @match        https://*.douban.com/*
// @match        https://*.x.com/*
// @match        https://*.pixiv.net/*
// @match        https://*.weibo.com/*
// @match        https://*.reddit.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // 平台数据定义
    const platformData = {
        zhihu: {
            name: '知乎',
            users: [],
            color: '#0084ff'
        },
        bilibili: {
            name: 'B站',
            users: [],
            color: '#fb7299'
        },
        douban: {
            name: '豆瓣',
            users: [],
            color: '#007722'
        },
        x: {
            name: 'X(推特)',
            users: [],
            color: '#000000'
        },
        pixiv: {
            name: 'Pixiv',
            users: [],
            color: '#0096fa'
        },
        weibo: {
            name: '微博',
            users: [],
            color: '#e6162d'
        },
        reddit: {
            name: 'Reddit',
            users: [],
            color: '#ff4500'
        }
    };

    // 获取当前平台
    const currentHost = window.location.hostname;
    let currentPlatform = 'zhihu'; // 默认

    if (currentHost.includes('bilibili')) {
        currentPlatform = 'bilibili';
    } else if (currentHost.includes('douban')) {
        currentPlatform = 'douban';
    } else if (currentHost.includes('x.com')) {
        currentPlatform = 'x';
    } else if (currentHost.includes('pixiv.net')) {
        currentPlatform = 'pixiv';
    } else if (currentHost.includes('weibo.com')) {
        currentPlatform = 'weibo';
    } else if (currentHost.includes('reddit.com')) {
        currentPlatform = 'reddit';
    }

    // 从存储中获取用户列表或使用初始列表
    const platformUsers = GM_getValue(`${currentPlatform}_users`, platformData[currentPlatform].users);

    // 创建主容器
    const container = document.createElement('div');
    container.id = 'multi-platform-panel';
    document.body.appendChild(container);

    // 颜色加深函数
    function darkenColor(color, percent) {
        const num = parseInt(color.replace('#', ''), 16);
        const amt = Math.round(2.55 * percent);
        const R = (num >> 16) - amt;
        const G = (num >> 8 & 0x00FF) - amt;
        const B = (num & 0x0000FF) - amt;
        return '#' + (
            0x1000000 +
            (R < 0 ? 0 : R) * 0x10000 +
            (G < 0 ? 0 : G) * 0x100 +
            (B < 0 ? 0 : B)
        ).toString(16).slice(1);
    }

    // 颜色变淡函数
    function lightenColor(color, percent) {
        const num = parseInt(color.replace('#', ''), 16);
        const amt = Math.round(2.55 * percent);
        const R = (num >> 16) + amt;
        const G = (num >> 8 & 0x00FF) + amt;
        const B = (num & 0x0000FF) + amt;
        return '#' + (
            0x1000000 +
            (R > 255 ? 255 : R) * 0x10000 +
            (G > 255 ? 255 : G) * 0x100 +
            (B > 255 ? 255 : B)
        ).toString(16).slice(1);
    }

    // 计算不同元素的颜色
    const primaryColor = platformData[currentPlatform].color;
    const headerColor = darkenColor(primaryColor, 10); // 导航标题使用深色
    const buttonColor = lightenColor(primaryColor, 10); // 超链接按钮使用浅色
    const actionButtonColor = primaryColor; // 添加/收藏按钮使用原色

    // 添加全局样式
    GM_addStyle(`
        #multi-platform-panel * {
            box-sizing: border-box;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
        }

        #expand-button {
            position: fixed;
            right: 0;
            top: 50%;
            transform: translateY(-50%);
            z-index: 9999;
            width: 20px;
            height: 40px;
            background-color: ${headerColor};
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 5px 0 0 5px;
            cursor: pointer;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            transition: all 0.3s ease;
        }

        #expand-button:hover {
            background-color: ${darkenColor(headerColor, 20)};
        }

        #panel-container {
            position: fixed;
            right: -300px;
            top: 50%;
            transform: translateY(-50%);
            z-index: 1000;
            width: 250px;
            background-color: rgba(255, 255, 255, 0.95);
            border-radius: 10px 0 0 10px;
            box-shadow: 0 2px 15px rgba(0, 0, 0, 0.2);
            transition: right 0.3s ease;
            max-height: 80vh;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }

        .panel-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10px 15px;
            background-color: ${headerColor};
            color: white;
        }

        .panel-title {
            font-weight: bold;
            font-size: 14px;
        }

        .close-button {
            cursor: pointer;
            font-size: 20px;
            line-height: 1;
            transition: transform 0.2s;
        }

        .close-button:hover {
            transform: scale(1.2);
        }

        #buttons-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 8px;
            padding: 15px;
            overflow-y: auto;
            max-height: calc(80vh - 150px);
            scrollbar-width: none;
            -ms-overflow-style: none;
        }

        #buttons-grid::-webkit-scrollbar {
            display: none;
        }

        .button-wrapper {
            position: relative;
            overflow: visible;
        }

        .user-button {
            display: block;
            padding: 6px 8px;
            background-color: ${buttonColor};
            color: white !important;
            text-align: center;
            border-radius: 4px;
            text-decoration: none;
            font-weight: 500;
            font-size: 12px;
            transition: all 0.3s;
            cursor: pointer;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            width: 100px;
        }

        .user-button:hover {
            background-color: ${darkenColor(buttonColor, 20)};
            transform: scale(1.05);
        }

        .delete-button {
            position: absolute;
            top: -5px;
            right: -5px;
            width: 16px;
            height: 16px;
            background-color: ${lightenColor(buttonColor, 30)};
            color: white;
            border-radius: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 12px;
            cursor: pointer;
            opacity: 0.7;
            transition: opacity 0.3s;
            z-index: 100;
            box-shadow: 0 1px 3px rgba(0,0,0,0.2);
        }

        .delete-button:hover {
            opacity: 1;
            background-color: ${lightenColor(buttonColor, 20)};
        }

        .action-button {
            padding: 8px 12px;
            color: white;
            text-align: center;
            border-radius: 5px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.3s;
            font-size: 13px;
            background-color: ${actionButtonColor};
            flex: 1;
        }

        .action-button:hover {
            background-color: ${darkenColor(actionButtonColor, 20)};
            transform: scale(1.05);
        }

        .button-container {
            display: flex;
            flex-direction: row;
            gap: 10px;
            padding: 0 15px 15px;
        }
    `);

    // 创建展开按钮
    const expandButton = document.createElement('div');
    expandButton.id = 'expand-button';
    expandButton.textContent = '▶';
    container.appendChild(expandButton);

    // 渲染面板函数
    function renderPanel() {
        // 移除旧的面板容器(如果存在)
        const oldPanel = document.getElementById('panel-container');
        if (oldPanel) oldPanel.remove();

        // 创建面板容器
        const panelContainer = document.createElement('div');
        panelContainer.id = 'panel-container';
        container.appendChild(panelContainer);

        // 添加标题和关闭按钮
        const header = document.createElement('div');
        header.className = 'panel-header';
        panelContainer.appendChild(header);

        const title = document.createElement('div');
        title.className = 'panel-title';
        title.textContent = `${platformData[currentPlatform].name}导航`;
        header.appendChild(title);

        const closeButton = document.createElement('div');
        closeButton.className = 'close-button';
        closeButton.textContent = '×';
        closeButton.onclick = () => {
            panelContainer.style.right = '-300px';
            expandButton.style.display = 'flex';
        };
        header.appendChild(closeButton);

        // 创建用户按钮网格容器
        const buttonsGrid = document.createElement('div');
        buttonsGrid.id = 'buttons-grid';
        panelContainer.appendChild(buttonsGrid);

        // 创建用户按钮
        platformUsers.forEach((user) => {
            const buttonWrapper = document.createElement('div');
            buttonWrapper.className = 'button-wrapper';
            buttonsGrid.appendChild(buttonWrapper);

            const button = document.createElement('a');
            button.className = 'user-button';
            button.href = user.url;
            button.target = '_blank';
            button.textContent = user.name;
            buttonWrapper.appendChild(button);

            const deleteButton = document.createElement('div');
            deleteButton.className = 'delete-button';
            deleteButton.innerHTML = '×';
            deleteButton.onclick = (e) => {
                e.preventDefault();
                e.stopPropagation();

                if (confirm(`确定要删除 "${user.name}" 吗?`)) {
                    const index = platformUsers.findIndex(u => u.name === user.name && u.url === user.url);
                    if (index !== -1) {
                        platformUsers.splice(index, 1);
                        GM_setValue(`${currentPlatform}_users`, platformUsers);
                        renderPanel();
                    }
                }
            };
            buttonWrapper.appendChild(deleteButton);

            // 当鼠标移入按钮区域时显示删除按钮
            buttonWrapper.onmouseenter = () => {
                deleteButton.style.display = 'flex';
            };

            buttonWrapper.onmouseleave = () => {
                deleteButton.style.display = 'none';
            };

            // 初始隐藏删除按钮
            deleteButton.style.display = 'none';
        });

        // 创建按钮容器
        const buttonContainer = document.createElement('div');
        buttonContainer.className = 'button-container';
        panelContainer.appendChild(buttonContainer);

        // 添加"添加"按钮
        const addButton = document.createElement('div');
        addButton.className = 'action-button';
        addButton.textContent = '添加';
        addButton.onclick = () => {
            const name = prompt('请输入用户名:');
            if (name === null) return;

            const url = prompt('请输入用户主页链接:');
            if (url === null) return;

            if (name && url) {
                platformUsers.push({ name, url });
                GM_setValue(`${currentPlatform}_users`, platformUsers);
                renderPanel();

                // 修复:添加后保持面板展开状态
                panelContainer.style.right = '0';
                expandButton.style.display = 'none';
            }
        };
        buttonContainer.appendChild(addButton);

        // 添加"收藏"按钮
        const collectButton = document.createElement('div');
        collectButton.className = 'action-button';
        collectButton.textContent = '收藏';
        collectButton.onclick = () => {
            const name = prompt('请输入收藏名称:', document.title);
            if (name === null) return;

            const url = window.location.href;

            if (name && url) {
                platformUsers.push({ name, url });
                GM_setValue(`${currentPlatform}_users`, platformUsers);
                renderPanel();

                // 修复:收藏后保持面板展开状态
                panelContainer.style.right = '0';
                expandButton.style.display = 'none';
            }
        };
        buttonContainer.appendChild(collectButton);

        // 展开按钮点击事件
        expandButton.onclick = () => {
            panelContainer.style.right = '0';
            expandButton.style.display = 'none';
        };
    }

    // 初始渲染
    renderPanel();
})();