您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 Linux.do 页面显示信任级别进度,并提供可自由编辑的自定义标签导航功能(URL自动生成)。
// ==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(); } })();