Livebench 模型对比工具

为Livebench排行榜添加模型选择和对比功能

// ==UserScript==
// @name         Livebench 模型对比工具
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  为Livebench排行榜添加模型选择和对比功能
// @author       schweigen
// @license      MIT
// @match        *://*.livebench.ai/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    console.log('Livebench 模型对比工具已加载');

    // 添加样式
    GM_addStyle(`
        .model-checkbox {
            margin-right: 8px;
            cursor: pointer;
            width: 18px;
            height: 18px;
        }
        .compare-controls {
            display: flex;
            gap: 10px;
            margin: 10px 0;
            padding: 10px;
            background: #f5f5f5;
            border: 1px solid #e0e0e0;
            border-radius: 5px;
            align-items: center;
            flex-wrap: wrap;
        }
        .compare-btn {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: all 0.3s;
        }
        .compare-btn-primary {
            background: #3b82f6;
            color: white;
        }
        .compare-btn-primary:hover {
            background: #2563eb;
        }
        .compare-btn-secondary {
            background: #6b7280;
            color: white;
        }
        .compare-btn-secondary:hover {
            background: #4b5563;
        }
        .compare-btn-danger {
            background: #ef4444;
            color: white;
        }
        .compare-btn-danger:hover {
            background: #dc2626;
        }
        .selected-count {
            font-size: 14px;
            color: #4b5563;
            margin-left: 10px;
        }
        .model-row-hidden {
            display: none !important;
        }
        .quick-select-group {
            display: flex;
            gap: 5px;
            align-items: center;
            margin-left: auto;
        }
        .quick-select-btn {
            padding: 4px 10px;
            font-size: 12px;
            background: #e5e7eb;
            color: #374151;
            border: 1px solid #d1d5db;
        }
        .quick-select-btn:hover {
            background: #d1d5db;
        }
        .model-checkbox-header {
            width: 40px;
            text-align: center;
            position: sticky;
            left: 0;
            background: white;
            z-index: 10;
        }
        td.model-checkbox-cell {
            width: 40px;
            text-align: center;
            position: sticky;
            left: 0;
            background: white;
            z-index: 9;
        }
    `);

    // 存储选中的模型
    let selectedModels = new Set(GM_getValue('selectedModels', []));
    let compareMode = false;
    let initialized = false;

    // 等待表格加载
    function waitForTable() {
        console.log('正在查找表格...');

        // 尝试多种选择器
        const selectors = [
            'table.main-tabl',
            'table.table',
            '.main-tabl',
            'table[class*="main"]',
            '.table-wrap table',
            '.scrollable-table table'
        ];

        let table = null;
        for (const selector of selectors) {
            table = document.querySelector(selector);
            if (table) {
                console.log(`找到表格,使用选择器: ${selector}`);
                break;
            }
        }

        if (table && !initialized) {
            // 确保表格有数据
            const rows = table.querySelectorAll('tbody tr');
            if (rows.length > 0) {
                console.log(`表格包含 ${rows.length} 行数据`);
                initialized = true;
                initializeCompareFeature(table);
            } else {
                console.log('表格为空,继续等待...');
                setTimeout(waitForTable, 1000);
            }
        } else if (!table) {
            console.log('未找到表格,继续等待...');
            setTimeout(waitForTable, 1000);
        }
    }

    function initializeCompareFeature(table) {
        console.log('开始初始化对比功能...');

        // 查找合适的容器
        let container = table.closest('.table-container') ||
                       table.closest('.scrollable-table') ||
                       table.parentElement;

        // 如果已经初始化过,先清理
        const existingPanel = document.querySelector('.compare-controls');
        if (existingPanel) {
            existingPanel.remove();
        }

        // 添加控制面板
        const controlPanel = createControlPanel();
        container.insertBefore(controlPanel, container.firstChild);
        console.log('控制面板已添加');

        // 检查是否已经添加过复选框列
        const existingCheckboxHeader = table.querySelector('.model-checkbox-header');
        if (!existingCheckboxHeader) {
            // 在表头添加复选框列
            const headerRow = table.querySelector('thead tr');
            if (headerRow) {
                const checkboxHeader = document.createElement('th');
                checkboxHeader.className = 'model-checkbox-header';
                checkboxHeader.innerHTML = '<input type="checkbox" id="select-all-models" title="全选/取消全选">';
                headerRow.insertBefore(checkboxHeader, headerRow.firstChild);
            }
        }

        // 为每一行添加复选框
        const rows = table.querySelectorAll('tbody tr');
        rows.forEach((row, index) => {
            // 检查是否已经有复选框
            if (row.querySelector('.model-checkbox-cell')) {
                return;
            }

            const modelCell = row.querySelector('.model-col') || row.querySelector('td:first-child');
            const modelLink = modelCell?.querySelector('a');
            const modelName = modelLink?.textContent || modelCell?.textContent || `Model ${index + 1}`;

            const checkboxCell = document.createElement('td');
            checkboxCell.className = 'model-checkbox-cell';

            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.className = 'model-checkbox';
            checkbox.checked = selectedModels.has(modelName.trim());
            checkbox.dataset.modelName = modelName.trim();

            checkboxCell.appendChild(checkbox);
            row.insertBefore(checkboxCell, row.firstChild);

            // 添加事件监听
            checkbox.addEventListener('change', () => {
                if (checkbox.checked) {
                    selectedModels.add(modelName.trim());
                } else {
                    selectedModels.delete(modelName.trim());
                }
                GM_setValue('selectedModels', Array.from(selectedModels));
                updateSelectedCount();
            });
        });

        // 全选功能
        const selectAllCheckbox = document.getElementById('select-all-models');
        if (selectAllCheckbox) {
            selectAllCheckbox.addEventListener('change', (e) => {
                const checkboxes = table.querySelectorAll('.model-checkbox');
                checkboxes.forEach(cb => {
                    cb.checked = e.target.checked;
                    const modelName = cb.dataset.modelName;
                    if (e.target.checked) {
                        selectedModels.add(modelName);
                    } else {
                        selectedModels.delete(modelName);
                    }
                });
                GM_setValue('selectedModels', Array.from(selectedModels));
                updateSelectedCount();
            });
        }

        updateSelectedCount();
        console.log('初始化完成!');
    }

    function createControlPanel() {
        const panel = document.createElement('div');
        panel.className = 'compare-controls';
        panel.innerHTML = `
            <button class="compare-btn compare-btn-primary" id="toggle-compare">
                只看选中的模型
            </button>
            <button class="compare-btn compare-btn-secondary" id="clear-selection">
                清除所有选择
            </button>
            <span class="selected-count" id="selected-count">已选择 0 个模型</span>

            <div class="quick-select-group">
                <span style="font-size: 12px; color: #6b7280;">快速选择:</span>
                <button class="compare-btn quick-select-btn" data-org="OpenAI">OpenAI</button>
                <button class="compare-btn quick-select-btn" data-org="Anthropic">Anthropic</button>
                <button class="compare-btn quick-select-btn" data-org="Google">Google</button>
                <button class="compare-btn quick-select-btn" data-top="10">Top 10</button>
                <button class="compare-btn quick-select-btn" data-reasoning="true">推理模型</button>
            </div>
        `;

        // 绑定事件
        panel.querySelector('#toggle-compare').addEventListener('click', toggleCompareMode);
        panel.querySelector('#clear-selection').addEventListener('click', clearSelection);

        // 快速选择按钮
        panel.querySelectorAll('.quick-select-btn').forEach(btn => {
            btn.addEventListener('click', () => handleQuickSelect(btn));
        });

        return panel;
    }

    function toggleCompareMode() {
        compareMode = !compareMode;
        const btn = document.getElementById('toggle-compare');

        if (compareMode) {
            btn.textContent = '显示所有模型';
            btn.classList.remove('compare-btn-primary');
            btn.classList.add('compare-btn-danger');
            applyCompareMode();
        } else {
            btn.textContent = '只看选中的模型';
            btn.classList.remove('compare-btn-danger');
            btn.classList.add('compare-btn-primary');
            showAllModels();
        }
    }

    function applyCompareMode() {
        const table = document.querySelector('table');
        const rows = table.querySelectorAll('tbody tr');

        rows.forEach(row => {
            const checkbox = row.querySelector('.model-checkbox');
            if (checkbox && !checkbox.checked) {
                row.classList.add('model-row-hidden');
            } else {
                row.classList.remove('model-row-hidden');
            }
        });
    }

    function showAllModels() {
        const rows = document.querySelectorAll('tbody tr');
        rows.forEach(row => {
            row.classList.remove('model-row-hidden');
        });
    }

    function clearSelection() {
        selectedModels.clear();
        GM_setValue('selectedModels', []);

        const checkboxes = document.querySelectorAll('.model-checkbox');
        checkboxes.forEach(cb => cb.checked = false);

        const selectAll = document.getElementById('select-all-models');
        if (selectAll) selectAll.checked = false;

        if (compareMode) {
            showAllModels();
        }

        updateSelectedCount();
    }

    function updateSelectedCount() {
        const count = selectedModels.size;
        const countElement = document.getElementById('selected-count');
        if (countElement) {
            countElement.textContent = `已选择 ${count} 个模型`;
        }
    }

    function handleQuickSelect(btn) {
        const table = document.querySelector('table');
        const rows = table.querySelectorAll('tbody tr');

        if (btn.dataset.org) {
            // 选择特定组织的模型
            const org = btn.dataset.org;
            rows.forEach(row => {
                const orgCell = row.querySelector('.organization-col') || row.querySelector('td:nth-child(3)');
                if (orgCell && orgCell.textContent.trim() === org) {
                    const checkbox = row.querySelector('.model-checkbox');
                    if (checkbox) {
                        checkbox.checked = true;
                        selectedModels.add(checkbox.dataset.modelName);
                    }
                }
            });
        } else if (btn.dataset.top) {
            // 选择前N个模型
            const n = parseInt(btn.dataset.top);
            Array.from(rows).slice(0, n).forEach(row => {
                const checkbox = row.querySelector('.model-checkbox');
                if (checkbox) {
                    checkbox.checked = true;
                    selectedModels.add(checkbox.dataset.modelName);
                }
            });
        } else if (btn.dataset.reasoning) {
            // 选择推理模型(包含thinking、reasoning、R1等关键词)
            const reasoningKeywords = ['thinking', 'Thinking', 'reasoning', 'Reasoning', 'R1', 'o3', 'o4'];
            rows.forEach(row => {
                const modelCell = row.querySelector('.model-col') || row.querySelector('td:nth-child(2)');
                const modelName = modelCell?.textContent || '';
                if (reasoningKeywords.some(keyword => modelName.includes(keyword))) {
                    const checkbox = row.querySelector('.model-checkbox');
                    if (checkbox) {
                        checkbox.checked = true;
                        selectedModels.add(checkbox.dataset.modelName);
                    }
                }
            });
        }

        GM_setValue('selectedModels', Array.from(selectedModels));
        updateSelectedCount();

        if (compareMode) {
            applyCompareMode();
        }
    }

    // 监听页面变化,处理动态加载
    const observer = new MutationObserver((mutations) => {
        if (!initialized) {
            waitForTable();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    // 开始执行
    setTimeout(waitForTable, 1000);

})();