NodeSeek 高级搜索 (自动主题)

为 NodeSeek 搜索结果页面添加高级功能,自动适配网站的亮色和暗色主题。

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         NodeSeek 高级搜索 (自动主题)
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  为 NodeSeek 搜索结果页面添加高级功能,自动适配网站的亮色和暗色主题。
// @author       Gemini
// @match        https://www.nodeseek.com/search*
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // --- 样式定义 ---
    // 使用网站自身的CSS变量,实现主题自适应
    GM_addStyle(`
        #advanced-search-controls {
            background: var(--c-panel-bg);
            border: 1px solid var(--c-border-color);
            color: var(--c-text-color);
            padding: 15px;
            margin-bottom: 20px;
            border-radius: 5px;
        }
        #advanced-search-controls h3 {
            margin-top: 0;
            margin-bottom: 10px;
            border-bottom: 1px solid var(--c-border-color);
            padding-bottom: 5px;
        }
        .control-group {
            margin-bottom: 10px;
        }
        .control-group label {
            font-weight: bold;
            display: block;
            margin-bottom: 5px;
        }
        .control-group input[type="text"],
        .control-group select {
            width: 100%;
            padding: 8px;
            box-sizing: border-box;
            background-color: var(--c-input-bg);
            border: 1px solid var(--c-input-border);
            border-radius: 3px;
            color: var(--c-text-color);
        }
        /* 适配下拉菜单选项的颜色 */
        .control-group select option {
            background-color: var(--c-panel-bg);
            color: var(--c-text-color);
        }
        #category-filters label {
            display: inline-block;
            margin-right: 15px;
            font-weight: normal;
        }
        #category-filters input {
            margin-right: 5px;
            vertical-align: middle;
        }
    `);

    // --- 创建高级搜索UI ---
    const controlPanel = document.createElement('div');
    controlPanel.id = 'advanced-search-controls';

    controlPanel.innerHTML = `
        <h3>高级搜索选项</h3>
        <div class="control-group">
            <label for="regex-filter">正则表达式标题过滤:</label>
            <input type="text" id="regex-filter" placeholder="例如: (OVH|KS-LE)">
        </div>
        <div class="control-group">
            <label for="user-filter">过滤用户 (用,分隔):</label>
            <input type="text" id="user-filter" placeholder="例如: user1,user2">
        </div>
        <div class="control-group">
            <label for="custom-sort">自定义排序:</label>
            <select id="custom-sort">
                <option value="default">默认排序</option>
                <option value="comments-desc">评论数 (降序)</option>
                <option value="comments-asc">评论数 (升序)</option>
            </select>
        </div>
        <div class="control-group">
            <label>板块过滤:</label>
            <div id="category-filters"></div>
        </div>
    `;

    // --- 插入UI到页面 ---
    const postListControler = document.querySelector('.post-list-controler');
    if (postListControler) {
        postListControler.parentNode.insertBefore(controlPanel, postListControler.nextSibling);
    } else {
        // 如果找不到控制器,就插入到帖子列表前
        const postList = document.querySelector('.post-list');
        if(postList) {
            postList.parentNode.insertBefore(controlPanel, postList);
        }
    }

    // --- 提取并创建板块过滤器 ---
    const categories = new Set();
    document.querySelectorAll('.post-list-item .post-category').forEach(el => {
        const categoryText = el.innerText.trim();
        if (categoryText) {
            categories.add(categoryText);
        }
    });

    const categoryFiltersContainer = document.getElementById('category-filters');
    if(categoryFiltersContainer) {
        categories.forEach(category => {
            const checkboxLabel = document.createElement('label');
            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.checked = true;
            checkbox.value = category;
            checkbox.className = 'category-filter-cb';
            checkboxLabel.appendChild(checkbox);
            checkboxLabel.appendChild(document.createTextNode(` ${category}`));
            categoryFiltersContainer.appendChild(checkboxLabel);
        });
    }


    // --- 获取所有帖子元素 ---
    const postList = document.querySelector('.post-list');
    if (!postList) return; // 如果没有帖子列表,则退出脚本

    const posts = Array.from(postList.querySelectorAll('.post-list-item'));
    if (posts.length === 0) return; // 如果没有帖子,也退出

    // 将原始顺序存储起来,用于恢复默认排序
    const originalPostsOrder = [...posts];


    // --- 核心过滤和排序函数 ---
    function applyFiltersAndSort() {
        const regexFilter = document.getElementById('regex-filter').value;
        const userFilter = document.getElementById('user-filter').value.split(',').map(u => u.trim().toLowerCase()).filter(u => u);
        const sortValue = document.getElementById('custom-sort').value;

        const checkedCategories = Array.from(document.querySelectorAll('.category-filter-cb:checked')).map(cb => cb.value);

        // 1. 应用过滤器
        posts.forEach(post => {
            const titleEl = post.querySelector('.post-title a');
            const userEl = post.querySelector('.info-author a');
            const categoryEl = post.querySelector('.post-category');

            // 健壮性检查,确保元素存在
            if (!titleEl || !userEl || !categoryEl) {
                post.style.display = ''; // 如果帖子结构不完整,则不隐藏
                return;
            }

            const title = titleEl.innerText;
            const user = userEl.innerText.trim().toLowerCase();
            const category = categoryEl.innerText.trim();

            let show = true;

            // 正则表达式过滤
            if (regexFilter) {
                try {
                    const regex = new RegExp(regexFilter, 'i');
                    if (!regex.test(title)) {
                        show = false;
                    }
                } catch (e) {
                    // 无效的正则表达式时,不在控制台报错,可以在输入框给提示
                }
            }

            // 用户过滤
            if (userFilter.length > 0 && userFilter.includes(user)) {
                show = false;
            }

            // 板块过滤
            if (!checkedCategories.includes(category)) {
                show = false;
            }

            post.style.display = show ? '' : 'none';
        });

        // 2. 应用排序
        let postsToSort = [...posts]; // 从原始帖子数组创建副本进行排序

        if (sortValue !== 'default') {
            postsToSort.sort((a, b) => {
                const commentsElA = a.querySelector('.info-comments-count span');
                const commentsElB = b.querySelector('.info-comments-count span');
                const commentsA = commentsElA ? (parseInt(commentsElA.title.split(' ')[0]) || 0) : 0;
                const commentsB = commentsElB ? (parseInt(commentsElB.title.split(' ')[0]) || 0) : 0;

                return sortValue === 'comments-desc' ? commentsB - commentsA : commentsA - commentsB;
            });
            // 重新排列DOM
            postsToSort.forEach(post => postList.appendChild(post));
        } else {
            // 恢复默认排序
             originalPostsOrder.forEach(post => postList.appendChild(post));
        }
    }

    // --- 添加事件监听器 ---
    const regexInput = document.getElementById('regex-filter');
    if (regexInput) regexInput.addEventListener('input', applyFiltersAndSort);

    const userInput = document.getElementById('user-filter');
    if (userInput) userInput.addEventListener('input', applyFiltersAndSort);

    const sortSelect = document.getElementById('custom-sort');
    if (sortSelect) sortSelect.addEventListener('change', applyFiltersAndSort);

    document.querySelectorAll('.category-filter-cb').forEach(cb => {
        cb.addEventListener('change', applyFiltersAndSort);
    });

})();