YOUR_SCRIPT_NAME

One-line description of what this script does.

当前为 2025-12-31 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         YOUR_SCRIPT_NAME
// @namespace    https://greasyfork.org/users/your-id
// @version      0.1.0
// @description  One-line description of what this script does.
// @author       You
// @license      MIT
// @match        https://example.com/*
// @run-at       document-idle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// ==/UserScript==

/**
 * GreasyFork-friendly guidelines used here:
 * - No obfuscation, no eval/new Function.
 * - Minimal @grant and permissions.
 * - Clear comments + predictable structure.
 * - Safe DOM operations + mutation observer cleanup.
 */
(() => {
  'use strict';

  // -----------------------------
  // Config / Feature flags
  // -----------------------------

  /** Toggle debug logs (default: false). */
  const DEBUG = false;

  /** Namespaced storage key prefix to avoid collisions with other scripts. */
  const STORE_PREFIX = 'your_script_name:';

  /** Simple logger wrapper to avoid noisy console. */
  const log = (...args) => { if (DEBUG) console.log('[YOUR_SCRIPT_NAME]', ...args); };

  // -----------------------------
  // Utilities
  // -----------------------------

  /**
   * Read value from Greasemonkey/Tampermonkey storage.
   * @template T
   * @param {string} key
   * @param {T} fallback
   * @returns {T}
   */
  function getStore(key, fallback) {
    try {
      return /** @type {any} */ (GM_getValue(STORE_PREFIX + key, fallback));
    } catch (e) {
      log('GM_getValue failed:', e);
      return fallback;
    }
  }

  /**
   * Save value to storage.
   * @param {string} key
   * @param {any} value
   */
  function setStore(key, value) {
    try {
      GM_setValue(STORE_PREFIX + key, value);
    } catch (e) {
      log('GM_setValue failed:', e);
    }
  }

  /**
   * Wait for an element to appear, then resolve.
   * Uses polling with timeout to stay simple & robust.
   * @param {string} selector
   * @param {{timeoutMs?: number, intervalMs?: number, root?: ParentNode}} [opts]
   * @returns {Promise<Element>}
   */
  function waitForSelector(selector, opts = {}) {
    const {
      timeoutMs = 10_000,
      intervalMs = 200,
      root = document
    } = opts;

    return new Promise((resolve, reject) => {
      const start = Date.now();

      const timer = setInterval(() => {
        const el = root.querySelector(selector);
        if (el) {
          clearInterval(timer);
          resolve(el);
          return;
        }
        if (Date.now() - start > timeoutMs) {
          clearInterval(timer);
          reject(new Error(`Timeout waiting for selector: ${selector}`));
        }
      }, intervalMs);
    });
  }

  /**
   * Add CSS safely.
   * @param {string} css
   */
  function addCss(css) {
    try {
      GM_addStyle(css);
    } catch {
      // Fallback for environments without GM_addStyle
      const style = document.createElement('style');
      style.textContent = css;
      document.head.appendChild(style);
    }
  }

  // -----------------------------
  // Core logic (replace with your own)
  // -----------------------------

  /**
   * Perform one-time initialization.
   * Put your main logic here.
   */
  async function main() {
    log('init');

    // Example: inject styles
    addCss(`
      /* YOUR_SCRIPT_NAME styles */
      .ysn-hidden { display: none !important; }
    `);

    // Example: wait for key element
    // (Replace selectors with your real target)
    try {
      const target = await waitForSelector('body');
      log('target ready:', target);

      // Example: apply once
      applyOnce();

      // Example: observe DOM changes if the site is SPA
      startObserver();
    } catch (e) {
      console.warn('[YOUR_SCRIPT_NAME] init failed:', e);
    }
  }

  /**
   * Apply changes once (idempotent).
   * Make sure repeated calls won't duplicate UI or listeners.
   */
  function applyOnce() {
    // Idempotency guard example
    const markerId = 'ysn-marker';
    if (document.getElementById(markerId)) return;

    const marker = document.createElement('div');
    marker.id = markerId;
    marker.style.display = 'none';
    document.documentElement.appendChild(marker);

    // TODO: Your actual one-time modifications
    // - Hide elements
    // - Add buttons
    // - Patch text
    log('applyOnce done');
  }

  /**
   * Mutation observer handler (keep fast!).
   * Avoid heavy querySelectorAll on every mutation.
   * @param {MutationRecord[]} mutations
   */
  function onMutation(mutations) {
    // Example: cheap detection strategy
    for (const m of mutations) {
      if (m.type === 'childList' && (m.addedNodes?.length || m.removedNodes?.length)) {
        // TODO: Your incremental updates
        // Tip: only touch relevant nodes or re-run small idempotent function
        // applyIncremental();
        break;
      }
    }
  }

  /** Keep observer reference for cleanup. */
  let observer = null;

  /** Start observing SPA page updates. */
  function startObserver() {
    if (observer) return;
    observer = new MutationObserver(onMutation);
    observer.observe(document.documentElement, { childList: true, subtree: true });
    log('observer started');
  }

  /** Stop observer if needed. */
  function stopObserver() {
    if (!observer) return;
    observer.disconnect();
    observer = null;
    log('observer stopped');
  }

  // -----------------------------
  // Boot
  // -----------------------------

  // If the site is heavy SPA, document-idle is usually safer.
  // You can also gate by readyState.
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', main, { once: true });
  } else {
    void main();
  }

  // Optional: cleanup when leaving page (rarely needed in userscripts)
  window.addEventListener('beforeunload', () => {
    stopObserver();
  });
})();