您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Provides an API for other userscripts to add tabs to a site's settings menu.
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/533630/1575889/Settings%20Tab%20Manager%20%28STM%29.js
// ==UserScript== // @name Settings Tab Manager (STM) // @namespace shared-settings-manager // @version 1.1.3 // @description Provides an API for other userscripts to add tabs to a site's settings menu. // @author nipah, Gemini // @license MIT // @match https://8chan.moe/* // @match https://8chan.se/* // @grant GM_addStyle // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // ... (Constants, State, Readiness Promise, Public API Definition, Styling remain the same) ... const log = (...args) => console.log(`[${MANAGER_ID}]`, ...args); const warn = (...args) => console.warn(`[${MANAGER_ID}]`, ...args); const error = (...args) => console.error(`[${MANAGER_ID}]`, ...args); const MANAGER_ID = 'SettingsTabManager'; const SELECTORS = {/* ... */}; const ACTIVE_CLASSES = {/* ... */}; const ATTRS = {/* ... */}; // ... (findSettingsElements, deactivateCurrentTab, activateTab, handleTabClick, attachTabClickListener remain the same) ... function handleTabClick(event) { /* ... same code (no stopPropagation) ... */ } function activateTab(scriptId) { /* ... same code ... */ } // --- REMOVED createSeparator function --- /** Creates and inserts the tab and panel elements for a given script config. (No Separators) */ function createTabAndPanel(config) { if (!tabContainerEl || !panelContainerEl) { error(`Cannot create tab/panel for ${config.scriptId}: Containers not found.`); return; } if (tabContainerEl.querySelector(`span[${ATTRS.SCRIPT_ID}="${config.scriptId}"]`)) { log(`Tab already exists for ${config.scriptId}, skipping creation.`); return; } log(`Creating tab/panel for: ${config.scriptId} (No Separator)`); // --- Create Tab --- const newTab = document.createElement('span'); newTab.className = SELECTORS.SITE_TAB.substring(1); // Use site's class newTab.textContent = config.tabTitle; newTab.setAttribute(ATTRS.SCRIPT_ID, config.scriptId); newTab.setAttribute(ATTRS.MANAGED, 'true'); newTab.setAttribute('title', `${config.tabTitle} (Settings by ${config.scriptId})`); const desiredOrder = typeof config.order === 'number' ? config.order : Infinity; newTab.setAttribute('data-stm-order', desiredOrder); // --- Create Panel --- const newPanel = document.createElement('div'); newPanel.className = SELECTORS.SITE_PANEL.substring(1); // Use site's class newPanel.setAttribute(ATTRS.SCRIPT_ID, config.scriptId); newPanel.setAttribute(ATTRS.MANAGED, 'true'); newPanel.id = `${MANAGER_ID}-${config.scriptId}-panel`; // --- Insertion Logic (Simplified: No Separators) --- let insertBeforeElement = null; const existingStmTabs = Array.from( tabContainerEl.querySelectorAll(`span[${ATTRS.MANAGED}][${ATTRS.SCRIPT_ID}]`) ).sort( // Sort based on order attribute (a, b) => parseInt(a.getAttribute('data-stm-order') || Infinity, 10) - parseInt(b.getAttribute('data-stm-order') || Infinity, 10) ); // Find the first existing STM tab with a higher order number for (const existingTab of existingStmTabs) { const existingOrder = parseInt(existingTab.getAttribute('data-stm-order') || Infinity, 10); if (desiredOrder < existingOrder) { insertBeforeElement = existingTab; break; } } // Insert the new tab if (insertBeforeElement) { // Insert before the found element tabContainerEl.insertBefore(newTab, insertBeforeElement); } else { // Append at the end (after all other STM tabs, potentially before native 'Other' etc.) // We could add logic here to find the last native tab if needed, // but appending might be sufficient. tabContainerEl.appendChild(newTab); } // Append Panel (Order doesn't matter visually) panelContainerEl.appendChild(newPanel); // --- Initialize Panel Content --- try { Promise.resolve(config.onInit(newPanel, newTab)).catch(e => { error(`Error during async onInit for ${config.scriptId}:`, e); newPanel.innerHTML = `<p style="color: red;">Error initializing settings panel for ${config.scriptId}. See console.</p>`; }); } catch (e) { error(`Error during sync onInit for ${config.scriptId}:`, e); newPanel.innerHTML = `<p style="color: red;">Error initializing settings panel for ${config.scriptId}. See console.</p>`; } } // ... (processPendingRegistrations, Initialization, Observer, API Implementation, Global Exposure remain the same) ... function initializeManager() { /* ... same code ... */ } const observer = new MutationObserver(/* ... */); // ... start observer ... // ... initializeManager() call ... // ... API Implementation functions (registerTabImpl etc.) ... // ... Global Exposure logic ... })();