LDStatus (v4.3.0)

在 Linux.do 页面显示信任级别进度,并提供可自由编辑的自定义标签导航功能(URL自动生成)。

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         LDStatus (v4.3.0)
// @namespace    http://tampermonkey.net/
// @version      4.3.0
// @description  在 Linux.do 页面显示信任级别进度,并提供可自由编辑的自定义标签导航功能(URL自动生成)。
// @author       1e0n (Leon) (modified by Wythe)
// @match        https://linux.do/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// @grant        GM_info
// @connect      connect.linux.do
// @connect      github.com
// @connect      raw.githubusercontent.com
// ==/UserScript==

(function() {
    'use strict';
    console.log("LDStatus (v4.3.0) script started!");

    // 创建样式
    const style = document.createElement('style');
    style.textContent = `
        /* --- 核心样式 (v3.6.1) --- */
        #ld-trust-level-panel.ld-dark-theme { background-color: #2d3748; color: #e2e8f0; box-shadow: 0 0 6px rgba(0, 0, 0, 0.4); }
        #ld-trust-level-panel.ld-dark-theme #ld-trust-level-header { background-color: #1a202c; color: white; }
        #ld-trust-level-panel.ld-dark-theme .ld-toggle-btn,
        #ld-trust-level-panel.ld-dark-theme .ld-refresh-btn,
        #ld-trust-level-panel.ld-dark-theme .ld-update-btn,
        #ld-trust-level-panel.ld-dark-theme .ld-theme-btn { color: white; }
        #ld-trust-level-panel.ld-dark-theme .ld-version { color: #a0aec0; }
        #ld-trust-level-panel.ld-dark-theme .ld-trust-level-item.ld-success .ld-value { color: #68d391; }
        #ld-trust-level-panel.ld-dark-theme .ld-trust-level-item.ld-fail .ld-value { color: #fc8181; }
        #ld-trust-level-panel.ld-dark-theme .ld-loading { color: #a0aec0; }
        #ld-trust-level-panel.ld-light-theme { background-color: #ffffff; color: #1a202c; box-shadow: 0 0 6px rgba(0, 0, 0, 0.15); border: 1px solid #e2e8f0; }
        #ld-trust-level-panel.ld-light-theme #ld-trust-level-header { background-color: #3182ce; color: #ffffff; border-bottom: 1px solid #2c5282; }
        #ld-trust-level-panel.ld-light-theme .ld-toggle-btn,
        #ld-trust-level-panel.ld-light-theme .ld-refresh-btn,
        #ld-trust-level-panel.ld-light-theme .ld-update-btn,
        #ld-trust-level-panel.ld-light-theme .ld-theme-btn { color: white; text-shadow: 0 0 1px rgba(0,0,0,0.3); }
        #ld-trust-level-panel.ld-light-theme .ld-version { color: #4a5568; }
        #ld-trust-level-panel.ld-light-theme .ld-trust-level-item.ld-success .ld-value { color: #276749; font-weight: bold; }
        #ld-trust-level-panel.ld-light-theme .ld-trust-level-item.ld-fail .ld-value { color: #c53030; font-weight: bold; }
        #ld-trust-level-panel.ld-light-theme .ld-name { color: #2d3748; }
        #ld-trust-level-panel.ld-light-theme .ld-loading { color: #4a5568; }
        #ld-trust-level-panel { position: fixed; left: 10px; top: 100px; width: 126px; border-radius: 6px; z-index: 9999; font-family: Arial, sans-serif; transition: all 0.3s ease; overflow: hidden; font-size: 12px; line-height: 1.2; }
        #ld-trust-level-header { padding: 1px 5px; cursor: move; user-select: none; }
        .ld-header-top-line { display: flex; justify-content: space-between; align-items: center; width: 100%; }
        .ld-header-title { font-weight: bold; white-space: nowrap; margin-right: 5px; }
        .ld-header-second-line { display: flex; justify-content: space-between; align-items: center; width: 100%; margin-top: 1px; }
        .ld-header-button-bar { display: flex; align-items: center; }
        #ld-trust-level-content { padding: 2px 5px; max-height: none; overflow-y: visible; }
        .ld-trust-level-item { margin-bottom: 2px; }
        .ld-trust-level-item .ld-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; margin-bottom: 0px; }
        .ld-trust-level-item .ld-value { display: block; font-weight: bold; text-align: left; }
        .ld-toggle-btn, .ld-refresh-btn, .ld-update-btn, .ld-theme-btn { background: none; border: none; cursor: pointer; font-size: 12px; padding: 1px; }
        .ld-toggle-btn { margin-left: 3px; }
        .ld-header-button-bar button { margin-left: 3px; }
        .ld-header-button-bar button:first-child { margin-left: 0; }
        .ld-version { font-size: 12px; font-weight: normal; }
        .ld-collapsed { width: 24px !important; height: 24px !important; min-width: 24px !important; max-width: 24px !important; border-radius: 6px; overflow: hidden; transform: none !important; line-height: initial; }
        .ld-collapsed #ld-trust-level-header { justify-content: center; width: 24px !important; height: 24px !important; min-width: 24px !important; max-width: 24px !important; padding: 0; display: flex; align-items: center; }
        .ld-collapsed #ld-trust-level-content { display: none !important; }
        .ld-collapsed .ld-header-title, .ld-collapsed .ld-header-second-line { display: none !important; }
        .ld-collapsed .ld-toggle-btn { margin: 0; font-size: 12px; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; padding: 0; }
        .ld-loading { text-align: center; padding: 5px; }
        .ld-dark-theme .ld-increase { color: #ffd700; }
        .ld-dark-theme .ld-decrease { color: #4299e1; }
        .ld-light-theme .ld-increase { color: #d69e2e; font-weight: bold; }
        .ld-light-theme .ld-decrease { color: #2b6cb0; font-weight: bold; }

        /* --- 样式 (v4.0.0 & v4.2.0) --- */
        .ld-separator { border: 0; height: 1px; margin: 4px 0; }
        .ld-dark-theme .ld-separator { background-color: #4a5568; }
        .ld-light-theme .ld-separator { background-color: #e2e8f0; }
        .ld-custom-tags-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }
        .ld-custom-tags-header .ld-title { font-weight: bold; }
        .ld-add-tag-btn { background: none; border: none; cursor: pointer; font-size: 14px; padding: 0 2px; line-height: 1; }
        .ld-light-theme .ld-add-tag-btn { color: #2c5282; }
        .ld-dark-theme .ld-add-tag-btn { color: #90cdf4; }
        .ld-custom-tag-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2px; }
        .ld-custom-tag-link { text-decoration: none; color: inherit; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-grow: 1; font-size: 12px; }
        .ld-dark-theme .ld-custom-tag-link:hover { color: white; text-decoration: underline; }
        .ld-light-theme .ld-custom-tag-link { color: #2b6cb0; }
        .ld-light-theme .ld-custom-tag-link:hover { color: #2c5282; text-decoration: underline; }
        .ld-delete-tag-btn { background: none; border: none; cursor: pointer; font-size: 12px; padding: 0 2px; color: #e53e3e; margin-left: 4px; line-height: 1; }
        .ld-delete-tag-btn:hover { color: #c53030; }
        .ld-credit-note { font-size: 10px; text-align: center; opacity: 0.7; margin-top: 5px; padding: 0 2px; }
        .ld-dark-theme .ld-credit-note { color: #a0aec0; }
        .ld-light-theme .ld-credit-note { color: #4a5568; }
    `;
    if (document.head) {
        document.head.appendChild(style);
    } else {
        document.addEventListener('DOMContentLoaded', () => document.head.appendChild(style));
    }

    const STORAGE_KEY_PREFIX = 'ld_panel_v4_';
    const STORAGE_KEY_POSITION = STORAGE_KEY_PREFIX + 'position';
    const STORAGE_KEY_COLLAPSED = STORAGE_KEY_PREFIX + 'collapsed';
    const STORAGE_KEY_THEME = STORAGE_KEY_PREFIX + 'theme';
    const STORAGE_KEY_PREVIOUS_REQ = STORAGE_KEY_PREFIX + 'previous_requirements';
    const STORAGE_KEY_CUSTOM_TAGS = STORAGE_KEY_PREFIX + 'custom_tags';

    const panel = document.createElement('div');
    panel.id = 'ld-trust-level-panel';

    const currentTheme = GM_getValue(STORAGE_KEY_THEME, 'dark');
    panel.classList.add(currentTheme === 'dark' ? 'ld-dark-theme' : 'ld-light-theme');

    let scriptVersion = "N/A";
    if (typeof GM_info !== 'undefined' && GM_info.script) {
        scriptVersion = GM_info.script.version || "N/A";
    }

    const header = document.createElement('div');
    header.id = 'ld-trust-level-header';
    header.innerHTML = `
        <div class="ld-header-top-line">
            <span class="ld-header-title">Status</span>
            <button class="ld-toggle-btn" title="展开/收起">◀</button>
        </div>
        <div class="ld-header-second-line">
            <span class="ld-version">v${scriptVersion}</span>
            <div class="ld-header-button-bar">
                <button class="ld-update-btn" title="检查更新">🔎</button>
                <button class="ld-refresh-btn" title="刷新数据">🔄</button>
                <button class="ld-theme-btn" title="切换主题">🌙</button>
            </div>
        </div>
    `;

    const content = document.createElement('div');
    content.id = 'ld-trust-level-content';
    content.innerHTML = '<div class="ld-loading">加载中...</div>';
    content.addEventListener('click', handleContentClick);


    panel.appendChild(header);
    panel.appendChild(content);

    if (document.body) {
        document.body.appendChild(panel);
    } else {
        document.addEventListener('DOMContentLoaded', () => document.body.appendChild(panel));
    }

    let toggleBtn, refreshBtn, updateBtn, themeBtn, versionSpan;
    let cachedRequirementsData = null;

    function queryHeaderElements() {
        toggleBtn = header ? header.querySelector('.ld-toggle-btn') : null;
        versionSpan = header ? header.querySelector('.ld-version') : null;
        const buttonBar = header ? header.querySelector('.ld-header-button-bar') : null;
        if (buttonBar) {
            updateBtn = buttonBar.querySelector('.ld-update-btn');
            refreshBtn = buttonBar.querySelector('.ld-refresh-btn');
            themeBtn = buttonBar.querySelector('.ld-theme-btn');
        }
    }
    queryHeaderElements();


    function savePanelPosition() { try { const transform = window.getComputedStyle(panel).transform; if (transform && transform !== 'none') { const matrix = new DOMMatrixReadOnly(transform); GM_setValue(STORAGE_KEY_POSITION, { x: matrix.e, y: matrix.f }); } } catch (e) { console.error("Error saving panel position:", e); } }
    function savePanelCollapsedState() { try { GM_setValue(STORAGE_KEY_COLLAPSED, panel.classList.contains('ld-collapsed')); } catch (e) { console.error("Error saving panel collapsed state:", e); } }

    function restorePanelState() {
        try {
            const isCollapsed = GM_getValue(STORAGE_KEY_COLLAPSED, false);
            if (isCollapsed) { panel.classList.add('ld-collapsed'); if (toggleBtn) toggleBtn.textContent = '▶'; } else { panel.classList.remove('ld-collapsed'); if (toggleBtn) toggleBtn.textContent = '◀'; }
            const position = GM_getValue(STORAGE_KEY_POSITION, null);
            if (position && typeof position.x === 'number' && typeof position.y === 'number') { panel.style.transform = `translate(${position.x}px, ${position.y}px)`; } else { panel.style.left = '10px'; panel.style.top = '100px'; panel.style.transform = ''; }
        } catch (e) {
            console.error("Error restoring panel state:", e);
            panel.classList.remove('ld-collapsed'); if (toggleBtn) toggleBtn.textContent = '◀'; panel.style.left = '10px'; panel.style.top = '100px'; panel.style.transform = '';
        }
    }

    let isDragging = false;
    let lastX, lastY;
    if (header) {
        header.addEventListener('mousedown', (e) => {
            if (panel.classList.contains('ld-collapsed') || e.target.closest('button')) return;
            isDragging = true;
            const currentTransform = window.getComputedStyle(panel).transform;
            const matrix = new DOMMatrixReadOnly(currentTransform === 'none' ? '' : currentTransform);
            lastX = e.clientX - matrix.e;
            lastY = e.clientY - matrix.f;
            panel.style.transition = 'none';
            document.body.style.userSelect = 'none';
        });
    }
    document.addEventListener('mousemove', (e) => { if (!isDragging) return; const newX = e.clientX - lastX; const newY = e.clientY - lastY; panel.style.transform = `translate(${newX}px, ${newY}px)`; });
    document.addEventListener('mouseup', () => { if (!isDragging) return; isDragging = false; panel.style.transition = ''; document.body.style.userSelect = ''; savePanelPosition(); });

    if (toggleBtn) { toggleBtn.addEventListener('click', () => { panel.classList.toggle('ld-collapsed'); toggleBtn.textContent = panel.classList.contains('ld-collapsed') ? '▶' : '◀'; savePanelCollapsedState(); }); }
    if (refreshBtn) refreshBtn.addEventListener('click', fetchTrustLevelData);
    if (updateBtn) updateBtn.addEventListener('click', checkForUpdates);
    if (themeBtn) themeBtn.addEventListener('click', toggleTheme);

    function toggleTheme() { const isDarkTheme = panel.classList.contains('ld-dark-theme'); panel.classList.remove(isDarkTheme ? 'ld-dark-theme' : 'ld-light-theme'); panel.classList.add(isDarkTheme ? 'ld-light-theme' : 'ld-dark-theme'); GM_setValue(STORAGE_KEY_THEME, isDarkTheme ? 'light' : 'dark'); updateThemeButtonIcon(); }
    function updateThemeButtonIcon() { if (!themeBtn) return; const isCurrentlyDark = panel.classList.contains('ld-dark-theme'); themeBtn.textContent = isCurrentlyDark ? '🌙' : '☀️'; themeBtn.title = isCurrentlyDark ? '切换为亮色主题' : '切换为深色主题'; }
    function checkForUpdates() { if (!updateBtn) return; const currentScriptVersion = (typeof GM_info !== 'undefined' && GM_info.script) ? GM_info.script.version : '0'; const updateMetaURL = (typeof GM_info !== 'undefined' && GM_info.script) ? GM_info.script.updateURL || GM_info.script.downloadURL : null; const downloadURL = (typeof GM_info !== 'undefined' && GM_info.script) ? GM_info.script.downloadURL || GM_info.script.updateURL : 'https://greasyfork.org/scripts/538282-ldstatus-%E6%89%8B%E6%9C%BA%E7%89%88.user.js'; if (!updateMetaURL) { updateBtn.textContent = '⚠️'; updateBtn.title = '无法获取更新信息URL'; return; } updateBtn.textContent = '⌛'; updateBtn.title = '正在检查更新...'; GM_xmlhttpRequest({ method: 'GET', url: updateMetaURL, onload: function(response) { if (response.status === 200) { const versionMatch = response.responseText.match(/@version\s+([\d\.]+)/); if (versionMatch && versionMatch[1]) { const remoteVersion = versionMatch[1]; if (remoteVersion > currentScriptVersion) { updateBtn.textContent = '⚠️'; updateBtn.title = `发现新版本 v${remoteVersion},点击更新`; updateBtn.style.color = '#ffd700'; updateBtn.onclick = function() { window.open(downloadURL, '_blank'); }; } else { updateBtn.textContent = '✔'; updateBtn.title = '已是最新版本'; updateBtn.style.color = '#68d391'; setTimeout(() => { updateBtn.textContent = '🔎'; updateBtn.title = '检查更新'; updateBtn.style.color = ''; updateBtn.onclick = checkForUpdates; }, 3000); } } else { handleUpdateError("无法解析远程版本号"); } } else { handleUpdateError(`请求失败: ${response.status}`); } }, onerror: function(error) { handleUpdateError(`网络错误: ${error.error}`);} }); function handleUpdateError(message = '检查更新失败') { updateBtn.textContent = '❌'; updateBtn.title = message; updateBtn.style.color = '#fc8181'; setTimeout(() => { updateBtn.textContent = '🔎'; updateBtn.title = '检查更新'; updateBtn.style.color = ''; }, 3000); } }

    let previousRequirements = [];
    try { const storedReqs = GM_getValue(STORAGE_KEY_PREVIOUS_REQ, null); if (storedReqs) previousRequirements = JSON.parse(storedReqs); } catch (e) { console.error("Error parsing stored previousRequirements:", e); GM_setValue(STORAGE_KEY_PREVIOUS_REQ, null); }

    function fetchTrustLevelData() { if (!content) { console.error("Content element not found for fetchTrustLevelData"); return; } content.innerHTML = '<div class="ld-loading">加载中...</div>'; GM_xmlhttpRequest({ method: 'GET', url: 'https://connect.linux.do', timeout: 15000, onload: function(response) { if (response.status === 200) { parseTrustLevelData(response.responseText); } else { content.innerHTML = `<div class="ld-loading">获取数据失败 (${response.status})</div>`; } }, onerror: function(error) { content.innerHTML = `<div class="ld-loading">获取数据失败 (网络错误: ${error.error || 'Unknown'})</div>`; }, ontimeout: function() { content.innerHTML = '<div class="ld-loading">获取数据超时</div>'; } }); }

    function parseTrustLevelData(html) {
        try {
            const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html');
            const trustLevelSection = Array.from(doc.querySelectorAll('.bg-white.p-6.rounded-lg')).find(div => { const heading = div.querySelector('h2'); return heading && heading.textContent.includes('信任级别'); });
            if (!trustLevelSection) { content.innerHTML = '<div class="ld-loading">未找到信任级别数据 (请登录 connect.linux.do)</div>'; return; }
            const heading = trustLevelSection.querySelector('h2').textContent.trim(); const match = heading.match(/(.*) - 信任级别 (\d+) 的要求/);
            const username = match ? match[1] : '未知用户'; const targetLevel = match ? match[2] : '未知';
            const tableRows = trustLevelSection.querySelectorAll('table tr'); const currentRequirements = [];
            for (let i = 1; i < tableRows.length; i++) {
                const row = tableRows[i]; const cells = row.querySelectorAll('td');
                if (cells.length >= 3) {
                    const name = cells[0].textContent.trim(); const current = cells[1].textContent.trim(); const required = cells[2].textContent.trim(); const isSuccess = cells[1].classList.contains('text-green-500'); const currentMatch = current.match(/(\d+)/); const currentValue = currentMatch ? parseInt(currentMatch[1], 10) : 0;
                    let changeValue = 0; let hasChanged = false;
                    if (previousRequirements && previousRequirements.length > 0) {
                        const prevReq = previousRequirements.find(pr => pr.name === name);
                        if (prevReq) { if (currentValue !== prevReq.currentValue) { changeValue = currentValue - prevReq.currentValue; hasChanged = true; } else if (prevReq.hasChanged && typeof prevReq.changeValue === 'number') { changeValue = prevReq.changeValue; hasChanged = true; } }
                    }
                    currentRequirements.push({ name, current, required, isSuccess, currentValue, changeValue, hasChanged });
                }
            }
            const resultText = trustLevelSection.querySelector('p.text-red-500, p.text-green-500'); const isMeetingRequirements = resultText ? !resultText.classList.contains('text-red-500') : false;

            cachedRequirementsData = { username, targetLevel, requirements: currentRequirements, isMeetingRequirements };
            renderFullPanelContent();

            previousRequirements = currentRequirements.map(r => ({ name: r.name, currentValue: r.currentValue, changeValue: r.changeValue, hasChanged: r.hasChanged }));
            GM_setValue(STORAGE_KEY_PREVIOUS_REQ, JSON.stringify(previousRequirements));
        } catch (e) { console.error("Error parsing trust level data:", e); content.innerHTML = '<div class="ld-loading">解析数据时出错</div>'; }
    }
    
    function renderFullPanelContent() {
        if (!content) return;
        if (!cachedRequirementsData) { content.innerHTML = '<div class="ld-loading">无缓存数据</div>'; return; }

        const { username, targetLevel, requirements, isMeetingRequirements } = cachedRequirementsData;

        let html = `
            <div style="margin-bottom: 2px; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="${username} - 信任级别 ${targetLevel}">
                ${username} - TL${targetLevel}
            </div>
            <div style="margin-bottom: 2px; font-weight: bold; font-size: 12px;" class="${isMeetingRequirements ? 'ld-success' : 'ld-fail'}">
                ${isMeetingRequirements ? '✔ 已符合' : '❌ 未符合'}要求
            </div>`;

        requirements.forEach(req => {
            let name = req.name.replace('已读帖子(所有时间)', '已读(总)').replace('浏览的话题(所有时间)', '浏览(总)').replace('获赞:点赞用户数量', '点赞用户').replace('获赞:单日最高数量', '总获赞天').replace('被禁言(过去 6 个月)', '被禁言').replace('被封禁(过去 6 个月)', '被封禁');
            let current = req.current.match(/(\d+)/) ? req.current.match(/(\d+)/)[1] : req.current;
            let required = req.required.match(/(\d+)/) ? req.required.match(/(\d+)/)[1] : req.required;
            let changeIndicator = '';
            if (req.hasChanged && typeof req.changeValue === 'number' && req.changeValue !== 0) {
                const diff = req.changeValue;
                changeIndicator = (diff > 0) ? `<span class="ld-increase"> ▲${diff}</span>` : `<span class="ld-decrease"> ▼${Math.abs(diff)}</span>`;
            }
            html += `<div class="ld-trust-level-item ${req.isSuccess ? 'ld-success' : 'ld-fail'}" title="${req.name}: ${req.current} / ${req.required}">
                        <span class="ld-name">${name}</span>
                        <span class="ld-value">${current}${changeIndicator} / ${required}</span>
                     </div>`;
        });

        html += `<hr class="ld-separator">`;

        const defaultTags = [ { name: "人工智能", url: "https://linux.do/tag/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD" }, { name: "订阅节点", url: "https://linux.do/tag/%E8%AE%A2%E9%98%85%E8%8A%82%E7%82%B9" } ];
        let customTags = [];
        try { customTags = JSON.parse(GM_getValue(STORAGE_KEY_CUSTOM_TAGS, JSON.stringify(defaultTags))); } catch (e) { customTags = defaultTags; }

        html += `<div class="ld-custom-tags-header">
                     <span class="ld-title">我的标签</span>
                     <button class="ld-add-tag-btn" title="添加新标签">➕</button>
                 </div>`;

        if (customTags.length > 0) {
            customTags.forEach((tag, index) => {
                html += `<div class="ld-custom-tag-item">
                            <a href="${tag.url}" target="_blank" class="ld-custom-tag-link" title="访问标签: ${tag.name}">${tag.name}</a>
                            <button class="ld-delete-tag-btn" data-index="${index}" title="删除此标签">✖</button>
                         </div>`;
            });
        } else {
            html += `<div style="font-size: 11px; text-align: center; opacity: 0.7; padding: 2px 0;">点击 ➕ 添加标签</div>`;
        }

        html += `<div class="ld-credit-note" title="本脚本由'零号'AI在原作者基础上修改">原作者: 1e0n(Leon), 基于其修改</div>`;

        content.innerHTML = html;
    }

    function handleContentClick(e) {
        if (e.target.matches('.ld-add-tag-btn')) { handleAddTag(); }
        if (e.target.matches('.ld-delete-tag-btn')) { const index = parseInt(e.target.getAttribute('data-index'), 10); handleDeleteTag(index); }
    }

    function handleAddTag() {
        const tagName = prompt("请输入新标签的名称:", "");
        if (!tagName || tagName.trim() === '') return;
        const trimmedName = tagName.trim();
        const finalURL = "https://linux.do/tag/" + encodeURIComponent(trimmedName);
        const defaultTags = [ { name: "人工智能", url: "https://linux.do/tag/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD" }, { name: "订阅节点", url: "https://linux.do/tag/%E8%AE%A2%E9%98%85%E8%8A%82%E7%82%B9" } ];
        let customTags = [];
        try { customTags = JSON.parse(GM_getValue(STORAGE_KEY_CUSTOM_TAGS, JSON.stringify(defaultTags))); } catch (e) { customTags = defaultTags; }
        customTags.push({ name: trimmedName, url: finalURL });
        GM_setValue(STORAGE_KEY_CUSTOM_TAGS, JSON.stringify(customTags));
        renderFullPanelContent();
    }

    function handleDeleteTag(index) {
        if (isNaN(index) || !confirm("确定要删除这个标签吗?")) return;
        const defaultTags = [ { name: "人工智能", url: "https://linux.do/tag/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD" }, { name: "订阅节点", url: "https://linux.do/tag/%E8%AE%A2%E9%98%85%E8%8A%82%E7%82%B9" } ];
        let customTags = [];
        try { customTags = JSON.parse(GM_getValue(STORAGE_KEY_CUSTOM_TAGS, JSON.stringify(defaultTags))); } catch (e) { customTags = defaultTags; }
        if (index >= 0 && index < customTags.length) {
            customTags.splice(index, 1);
            GM_setValue(STORAGE_KEY_CUSTOM_TAGS, JSON.stringify(customTags));
            renderFullPanelContent();
        }
    }


    function initializePanel() {
        if (!panel || !header || !content || !document.body || !document.head) {
            console.warn("LDStatus: Panel elements not fully ready, retrying init.");
            setTimeout(initializePanel, 50);
            return;
        }
        console.log("LDStatus: Initializing panel fully.");
        queryHeaderElements();
        restorePanelState();
        updateThemeButtonIcon();
        fetchTrustLevelData();
        setInterval(fetchTrustLevelData, 300000); // 5-minute refresh
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initializePanel);
    } else {
        initializePanel();
    }

})();