Guru Tools

Guru Tools for Fishtank.LIVE

目前為 2025-12-14 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Guru Tools
// @description  Guru Tools for Fishtank.LIVE
// @version      1.2.0
// @author       phungus
// @homepageURL  https://fishtank.guru
// @namespace    https://fishtank.guru
// @supportURL   https://discord.gg/2pMhfu7TwF
// @license      GPL-3.0-or-later
// @icon         https://www.google.com/s2/favicons?sz=64&domain=fishtank.live
// @match        https://www.fishtank.live/*
// @grant        GM_addStyle
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';
  const KEY_PREFIX = 'guruTools:';
  const PLUGIN_VERSION = '1.2.0';
  const META_URL = 'https://greasyfork.org/scripts/557589-guru-tools/code/Guru%20Tools.meta.js';
  const idFor = (t, i) => `option_${t}_${i}`;
  const save = (id, val) => localStorage.setItem(KEY_PREFIX + id, typeof val === 'boolean' ? (val ? 'true' : 'false') : String(val));
  const load = (id) => {
    const v = localStorage.getItem(KEY_PREFIX + id);
    if (v === 'true') return true;
    if (v === 'false') return false;
    return v;
  };

  GM_addStyle(`
    #guruToolsBtn{cursor:pointer;display:flex;align-items:center;justify-content:center;text-transform:uppercase;padding:6px 8px;border:1px solid #505050;border-radius:4px;color:#fff;box-shadow:4px 4px 0 rgba(0,0,0,.5);gap:8px;letter-spacing:-1px;background-color:rgba(115,6,0,.5);border-color:rgba(243,14,0,.25);width:100%;margin:0;}
    #guruToolsBtn:hover{background-color:rgba(115,6,0,.7);}
    #guruToolsBtnIcon{width:16px;height:16px;margin-right:6px;vertical-align:middle;filter:drop-shadow(2px 2px 0 rgba(0,0,0,.75));transition:filter .2s ease;}
    #guruToolsBtn:hover #guruToolsBtnIcon{animation:guruSpin 1s linear infinite;filter:none;}
    #guruToolsBtn span{font-size:16px !important;font-weight:400 !important;line-height:20px !important;text-transform:uppercase;}
    @keyframes guruSpin{0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}}
    #guruOverlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:2147483600;}
    #guruModal{display:none;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:640px;height:420px;border-radius:10px;color:#fff;z-index:2147483601;background:linear-gradient(-45deg,#ee7752,#e73c7e,#23a6d5,#23d5ab);background-size:400% 400%;animation:gradientBG 15s ease infinite;box-shadow:0 0 25px rgba(0,0,0,0.6);overflow:hidden;display:flex;flex-direction:column;max-width:90vw;max-height:90vh;}
    #guruHeader{display:flex;justify-content:space-between;align-items:center;background:rgba(0,0,0,0.4);height:50px;padding:0;}
    #guruHeaderLeft{display:flex;align-items:center;gap:8px;padding-left:10px;}
    #guruHeaderLeft img{height:28px;display:block;}
    #guruVersion{font-size:8px;color:#fff;opacity:0.85;text-shadow:none;}
    #guruHeaderRight{display:flex;align-items:center;gap:6px;margin-left:auto;}
    #guruModalClose{cursor:pointer;font-size:22px;font-weight:bold;color:#fff;background:tomato;height:100%;padding:0 18px;line-height:50px;transition:background 0.2s ease;}
    #guruModalClose:hover{background:#e5533f;}
    #guruUpdateBtn{cursor:pointer;font-size:8px;font-family:Arial,sans-serif;font-weight:900;color:#fff;background:#4caf50;padding:3px 8px;border-radius:20px;line-height:normal;display:none;align-items:center;justify-content:center;transition:background 0.2s ease;text-shadow:none;}
    #guruUpdateBtn:hover{background:#45a049;}
    #guruTabs{display:flex;flex-wrap:wrap;background:rgba(0,0,0,0.6);gap:0;padding:0;align-content:stretch;}
    #guruTabs button{flex:1 1 auto;min-width:80px;border:none;margin:0;border-radius:0;background:transparent;color:#fff;cursor:pointer;font-family:Arial,sans-serif;font-weight:700;text-transform:uppercase;font-size:12px;line-height:50px;transition:background 0.2s ease;display:flex;align-items:center;justify-content:center;gap:8px;padding:0 6px;}
    #guruTabs button:hover{background:rgba(255,255,255,0.08);}
    #guruTabs button.active{background:rgba(0,0,0,0.8);}
    #guruTabs .tabIcon{font-size:16px;line-height:1;}
    .guruTabContent{display:none;padding:16px 20px;flex:1;overflow-y:auto;background:rgba(0,0,0,0.35);}
    .guruTabContent.active{display:block;}
    #tab1.guruTabContent{padding:5px 20px !important;}
    #tab2.guruTabContent,#tab3.guruTabContent,#tab4.guruTabContent,#tab5.guruTabContent{padding:0 !important;margin:0 !important;overflow:hidden;background:none;}
    #tab2.guruTabContent iframe,#tab3.guruTabContent iframe,#tab4.guruTabContent iframe,#tab5.guruTabContent iframe{width:100%;height:100%;border:none;margin:0;padding:0;display:block;background:transparent !important;}
    .guruOption{margin:14px 0;display:flex;align-items:center;cursor:pointer;padding:6px;border-radius:4px;transition:background 0.2s ease;}
    .guruOption:hover{background:rgba(255,255,255,0.10);}
    .switch{position:relative;width:50px;height:24px;flex-shrink:0;}
    .switch input{opacity:0;width:0;height:0;}
    .slider{position:absolute;inset:0;background-color:#ccc;border-radius:24px;}
    .slider:before{position:absolute;content:"";height:18px;width:18px;left:3px;bottom:3px;background-color:white;border-radius:50%;transition:.2s;}
    input:checked + .slider{background-color:#4CAF50;}
    input:checked + .slider:before{transform:translateX(26px);}
    .guruOptionText{margin-left:16px;}
    .guruOptionTitle{font-family:Arial,sans-serif;font-weight:900;font-size:14px;text-shadow:none;}
    .guruOptionDesc{font-size:11px;color:#ccc;margin-top:4px;line-height:1.4;text-shadow:none;}
    @keyframes gradientBG{0%{background-position:0% 50%;}50%{background-position:100% 50%;}100%{background-position:0% 50%;}}
    #guruSnowOverlay{position:fixed;inset:0;pointer-events:none;z-index:2147483599;}
    #guruSantaHat{position:absolute;}
    @media screen and (max-height:942px), screen and (max-width:1101px){#guruSantaHat{display:none !important;}}
    .top-bar_logo__XL0_C{position:relative;}
    body.guru-extend-inventory .inventory_slots__D4IrC{max-height:none !important;}
    body.guru-wartoy-protections .chat-message-default_shrink-ray__nGvpr{font-size:6px !important;}
    body.guru-wartoy-protections.mirror{transform:scaleY(1) !important;}
    body.guru-wartoy-protections .live-stream-player_blur__7BhBE video{filter:blur(0px) !important;}
    body.guru-wartoy-protections.blind{filter:grayscale(0) blur(0) !important;}
    body.guru-hide-season-pass .toast_season-pass__cmkhU,
    body.guru-hide-season-pass .experience-daily-login_season-pass__YTtsY:has(.icon_icon__bDzMA),
    body.guru-hide-season-pass .item-generator_item-generator__TCQ9l{display:none !important;}
    body.guru-hide-ads .ads_ads__Z1cPk{display:none !important;}
    body.guru-hide-applications .applications-alert_applications-alert__3zfnO{display:none !important;}
    #guruItemDexOptions{display:flex;justify-content:space-between;align-items:center;padding:4px 10px;background:linear-gradient(-45deg,#ee7752,#e73c7e,#23a6d5,#23d5ab);background-size:400% 400%;animation:gradientBG 15s ease infinite;border-radius:4px;color:#fff;font-size:13px;font-family:Arial,sans-serif;font-weight:700;width:100%;}
    #guruItemDexOptions .leftLabel{font-size:14px;font-weight:900;letter-spacing:.5px;text-shadow:none;}
    #guruItemDexOptions .options{display:flex;gap:12px;align-items:center;flex-wrap:wrap;}
    #guruItemDexOptions .options label{display:flex;align-items:center;gap:6px;cursor:pointer;font-weight:400;text-shadow:none;}
    #guruItemDexOptions input[type="checkbox"]{transform:scale(1.1);margin:0;}
  `);

  const overlay = document.createElement('div');
  overlay.id = 'guruOverlay';
  document.body.appendChild(overlay);

  const modal = document.createElement('div');
  modal.id = 'guruModal';
  overlay.appendChild(modal);

  function openModal(e) { if (e) e.stopPropagation(); overlay.style.display = 'block'; modal.style.display = 'flex'; }
  function closeModal(e) { if (e) e.stopPropagation(); overlay.style.display = 'none'; modal.style.display = 'none'; }

  function generateOptions(tabNum) {
    let html = '';
    const count = tabNum === 1 ? 7 : 5;
    for (let i = 1; i <= count; i++) {
      const id = idFor(tabNum, i);
      let title = `Option ${tabNum}.${i}`;
      let desc = `This is a description for option ${tabNum}.${i}.`;
      if (tabNum === 1 && i === 1) {
        title = 'Extended Inventory Box';
        desc = 'Extends your inventory items list so you don’t have to scroll.';
        html += `
          <div class="guruOption" data-id="${id}">
            <label class="switch">
              <input type="checkbox" id="${id}">
              <span class="slider"></span>
            </label>
            <div class="guruOptionText">
              <div class="guruOptionTitle">${title}</div>
              <div class="guruOptionDesc">${desc}</div>
            </div>
          </div>`;
        const sorterId = idFor(1, 7);
        html += `
          <div class="guruOption" data-id="${sorterId}">
            <label class="switch">
              <input type="checkbox" id="${sorterId}">
              <span class="slider"></span>
            </label>
            <div class="guruOptionText">
              <div class="guruOptionTitle">Item Dex Sorter</div>
              <div class="guruOptionDesc">Sort the list of consumed items on profiles.</div>
            </div>
          </div>`;
        continue;
      }
      if (tabNum === 1 && i === 2) { title = 'Wartoy Protections'; desc = 'Undo the effects of some wartoys such as Color Blind, Shrink Ray, Adjust Focus and Mirror Universe.'; }
      else if (tabNum === 1 && i === 3) { title = 'Hide Season Pass Popups'; desc = 'Blocks the Season Pass popup advertisements and buttons.'; }
      else if (tabNum === 1 && i === 4) { title = 'Hide Advertisements'; desc = 'Hides the advertisements box in the left panel.'; }
      else if (tabNum === 1 && i === 5) { title = 'Hide Contestant Applications Popup'; desc = 'Hides the popup for Season 5 contestant applications.'; }
      else if (tabNum === 1 && i === 6) { title = 'Holiday Spirit'; desc = 'Adds decorations to the site during certain times of the year.'; }
      else if (tabNum === 1 && i === 7) { continue; }
      html += `
        <div class="guruOption" data-id="${id}">
          <label class="switch">
            <input type="checkbox" id="${id}">
            <span class="slider"></span>
          </label>
          <div class="guruOptionText">
            <div class="guruOptionTitle">${title}</div>
            <div class="guruOptionDesc">${desc}</div>
          </div>
        </div>`;
    }
    return html;
  }

  modal.innerHTML = `
    <div id="guruHeader">
      <div id="guruHeaderLeft">
        <a href="https://fishtank.guru" target="_blank" rel="noopener noreferrer">
          <img src="https://fishtank.guru/wp-content/uploads/2024/06/fishtank-live-guru-logo-2024.png" alt="Guru Logo">
        </a>
        <span id="guruVersion">v${PLUGIN_VERSION}</span>
      </div>
      <div id="guruHeaderRight">
        <span id="guruUpdateBtn">Update Available!</span>
        <span id="guruModalClose">✖</span>
      </div>
    </div>
    <div id="guruTabs">
      <button class="active" data-tab="tab1"><span class="tabIcon">⚙️</span><span>Options</span></button>
      <button data-tab="tab2"><span class="tabIcon">🛠️</span><span>Crafting</span></button>
      <button data-tab="tab3"><span class="tabIcon">🧸</span><span>Items</span></button>
      <button data-tab="tab4"><span class="tabIcon">🏆</span><span>Medals</span></button>
      <button data-tab="tab5"><span class="tabIcon">🫡</span><span>Emotes</span></button>
    </div>
    <div id="tab1" class="guruTabContent active">${generateOptions(1)}</div>
    <div id="tab2" class="guruTabContent"><iframe src="https://fishtank.guru/crafting/lite"></iframe></div>
    <div id="tab3" class="guruTabContent"><iframe src="https://fishtank.guru/items/lite"></iframe></div>
    <div id="tab4" class="guruTabContent"><iframe src="https://fishtank.guru/medals/lite"></iframe></div>
    <div id="tab5" class="guruTabContent"><iframe src="https://fishtank.guru/emotes/lite"></iframe></div>
  `;

  modal.querySelector('#guruModalClose').addEventListener('click', closeModal);
  overlay.addEventListener('click', (e) => { if (e.target === overlay) closeModal(e); });

  (function initTabs() {
    const tabButtons = modal.querySelectorAll('#guruTabs button');
    const tabContents = modal.querySelectorAll('.guruTabContent');
    tabButtons.forEach((b) => {
      b.addEventListener('click', () => {
        tabButtons.forEach((tb) => tb.classList.remove('active'));
        tabContents.forEach((tc) => tc.classList.remove('active'));
        b.classList.add('active');
        const panel = modal.querySelector(`#${b.dataset.tab}`);
        if (panel) panel.classList.add('active');
      });
    });
  })();

  const HOLIDAY_SNOW_ID = 'holidaySnowLink';
  const HOLIDAY_FIX_ID = 'holidaySnowFix';
  const HOLIDAY_OVERLAY_ID = 'guruSnowOverlay';
  const HOLIDAY_SNOW_URL = 'https://fishtank.guru/resources/elements/snow.css';
  const FLAKE_CLASS = 'snow';
  const FLAKE_ATTR = 'data-guru-snow';
  const FLAKE_COUNT = 200;
  const SANTA_HAT_ID = 'guruSantaHat';
  const SANTA_HAT_URL = 'https://fishtank.guru/resources/Santa%20Hat.png';

  function inHolidayWindow(d) {
    const year = d.getFullYear();
    const start = new Date(year, 10, 30, 0, 0, 0, 0);
    const end = new Date(year + 1, 0, 1, 23, 59, 59, 999);
    return d >= start && d <= end;
  }

  function ensureSnowCSS(enabled) {
    const link = document.getElementById(HOLIDAY_SNOW_ID);
    const fix = document.getElementById(HOLIDAY_FIX_ID);
    if (enabled) {
      if (!link) {
        const l = document.createElement('link');
        l.id = HOLIDAY_SNOW_ID;
        l.rel = 'stylesheet';
        l.href = HOLIDAY_SNOW_URL;
        document.head.appendChild(l);
      }
      if (!fix) {
        const f = document.createElement('style');
        f.id = HOLIDAY_FIX_ID;
        f.textContent = `.snow{pointer-events:none;}`;
        document.head.appendChild(f);
      }
    } else {
      if (link) link.remove();
      if (fix) fix.remove();
    }
  }

  function ensureSnowDOM(enabled) {
    let overlayEl = document.getElementById(HOLIDAY_OVERLAY_ID);
    if (enabled) {
      if (!overlayEl) {
        overlayEl = document.createElement('div');
        overlayEl.id = HOLIDAY_OVERLAY_ID;
        document.body.appendChild(overlayEl);
      }
      const current = overlayEl.querySelectorAll(`.${FLAKE_CLASS}[${FLAKE_ATTR}="1"]`).length;
      if (current < FLAKE_COUNT) {
        for (let i = current; i < FLAKE_COUNT; i++) {
          const flake = document.createElement('div');
          flake.className = FLAKE_CLASS;
          flake.setAttribute(FLAKE_ATTR, '1');
          overlayEl.appendChild(flake);
        }
      } else if (current > FLAKE_COUNT) {
        const flakes = overlayEl.querySelectorAll(`.${FLAKE_CLASS}[${FLAKE_ATTR}="1"]`);
        for (let i = FLAKE_COUNT; i < flakes.length; i++) flakes[i].remove();
      }
    } else {
      if (overlayEl) overlayEl.remove();
    }
  }

  function ensureSantaHat(enabled) {
    const logoBtn = document.querySelector('.top-bar_logo__XL0_C');
    if (!logoBtn) return;
    let hat = document.getElementById(SANTA_HAT_ID);
    if (enabled) {
      if (!hat) {
        hat = document.createElement('img');
        hat.id = SANTA_HAT_ID;
        hat.src = SANTA_HAT_URL;
        logoBtn.appendChild(hat);
      }
      hat.style.position = 'absolute';
      hat.style.top = '-10px';
      hat.style.left = 'calc(50% + 70px)';
      hat.style.transform = 'translateX(-50%) rotate(-10deg) scaleX(-1)';
      hat.style.width = '60px';
      hat.style.pointerEvents = 'none';
      hat.style.zIndex = '2147483602';
      hat.style.filter = 'drop-shadow(2px 2px 2px rgba(0,0,0,0.3))';
    } else {
      if (hat) hat.remove();
    }
  }

  function updateHolidaySpirit(isOn) {
    const active = isOn && inHolidayWindow(new Date());
    ensureSnowCSS(active);
    ensureSnowDOM(active);
    ensureSantaHat(active);
  }

  function applyItemFilters() {
    const hideConsumed = document.querySelector('#guruHideConsumed')?.checked;
    const hideFishtoys = document.querySelector('#guruHideFishtoys')?.checked;
    const blocked = [
      "Send_a_Rose","Plushie_Delivery","Toy_Delivery","Love_Letter","Snack_Delivery",
      "babel-fish","mirror","blind","shrink-ray","three-fifths-alt","heroic-sacrifice",
      "keyboard","deface","piranhas","finge-2","fishbnb-2","kamikaze-strike","assassin","military"
    ];
    document.querySelectorAll('.user-profile-items_item__9ECcd').forEach(item => {
      let hide = false;
      if (hideConsumed) {
        const timesIcon = item.querySelector('.user-profile-items_times__Ko05l');
        if (timesIcon) hide = true;
      }
      if (hideFishtoys) {
        const img = item.querySelector('img.user-profile-items_icon__zK0AB');
        if (img && blocked.some(key => img.src.includes(key))) hide = true;
      }
      item.style.display = hide ? 'none' : '';
    });
  }

  function toggleItemDexSorter(enabled) {
    const container = document.querySelector('.user-profile-items_user-profile-items__rl_CV');
    const box = document.getElementById('guruItemDexOptions');
    if (enabled && container) {
      if (!box) {
        const newBox = document.createElement('div');
        newBox.id = 'guruItemDexOptions';
        newBox.innerHTML = `
          <div class="leftLabel">Guru Tools</div>
          <div class="options">
            <label>
              <input type="checkbox" id="guruHideConsumed">
              <span>Hide Consumed Items</span>
            </label>
            <label>
              <input type="checkbox" id="guruHideFishtoys">
              <span>Hide Fishtoys</span>
            </label>
          </div>
        `;
        container.insertAdjacentElement('beforebegin', newBox);
        const chkConsumed = newBox.querySelector('#guruHideConsumed');
        const chkFishtoys = newBox.querySelector('#guruHideFishtoys');
        const persistedConsumed = load('itemdex:hideConsumed');
        const persistedFishtoys = load('itemdex:hideFishtoys');
        if (persistedConsumed === true) chkConsumed.checked = true;
        if (persistedFishtoys === true) chkFishtoys.checked = true;
        chkConsumed.addEventListener('change', () => { save('itemdex:hideConsumed', chkConsumed.checked); applyItemFilters(); });
        chkFishtoys.addEventListener('change', () => { save('itemdex:hideFishtoys', chkFishtoys.checked); applyItemFilters(); });
      }
      applyItemFilters();
    } else {
      if (box) box.remove();
      document.querySelectorAll('.user-profile-items_item__9ECcd').forEach(item => item.style.display = '');
    }
  }

  function applyPersistedOptionClasses() {
    document.body.classList.toggle('guru-extend-inventory', load(idFor(1, 1)));
    document.body.classList.toggle('guru-wartoy-protections', load(idFor(1, 2)));
    document.body.classList.toggle('guru-hide-season-pass', load(idFor(1, 3)));
    document.body.classList.toggle('guru-hide-ads', load(idFor(1, 4)));
    document.body.classList.toggle('guru-hide-applications', load(idFor(1, 5)));
    updateHolidaySpirit(load(idFor(1, 6)));
    toggleItemDexSorter(load(idFor(1, 7)));
  }

  function wireOptions() {
    const inputs = modal.querySelectorAll('input[type="checkbox"]');
    inputs.forEach((input) => {
      const id = input.id;
      const persisted = load(id);
      if (persisted === true) input.checked = true;
      input.addEventListener('change', (e) => {
        const checked = e.target.checked;
        save(id, checked);
        if (id === idFor(1, 1)) document.body.classList.toggle('guru-extend-inventory', checked);
        if (id === idFor(1, 2)) document.body.classList.toggle('guru-wartoy-protections', checked);
        if (id === idFor(1, 3)) document.body.classList.toggle('guru-hide-season-pass', checked);
        if (id === idFor(1, 4)) document.body.classList.toggle('guru-hide-ads', checked);
        if (id === idFor(1, 5)) document.body.classList.toggle('guru-hide-applications', checked);
        if (id === idFor(1, 6)) updateHolidaySpirit(checked);
        if (id === idFor(1, 7)) toggleItemDexSorter(checked);
      });
      const optionDiv = input.closest('.guruOption');
      if (optionDiv) {
        optionDiv.addEventListener('click', () => { input.checked = !input.checked; input.dispatchEvent(new Event('change')); });
        input.addEventListener('click', (e) => e.stopPropagation());
        const slider = optionDiv.querySelector('.slider');
        if (slider) slider.addEventListener('click', (e) => e.stopPropagation());
      }
    });
  }

  const overlayInit = () => {
    const container = document.querySelector('.layout_left__O2uku');
    if (!container) return;
    let btn = container.querySelector('#guruToolsBtn');
    if (!btn) {
      btn = document.createElement('button');
      btn.id = 'guruToolsBtn';
      btn.innerHTML = `
        <img id="guruToolsBtnIcon" src="https://fishtank.guru/resources/icons/gurutoolsicon.svg" alt="Guru Tools Icon" />
        <span>Guru Tools</span>
      `;
      container.insertBefore(btn, container.firstChild);
      btn.addEventListener('click', openModal);
      btn._gtBound = true;
    } else if (!btn._gtBound) {
      btn.addEventListener('click', openModal);
      btn._gtBound = true;
    }
  };

  function showUpdateButton() {
    const btn = document.getElementById('guruUpdateBtn');
    if (btn) {
      btn.style.display = 'inline-flex';
      if (!btn._gtBound) {
        btn.addEventListener('click', () => {
          window.open('https://greasyfork.org/en/scripts/557589-guru-tools', '_blank');
        });
        btn._gtBound = true;
      }
    }
  }

  async function checkForUpdate() {
    try {
      const res = await fetch(META_URL, { cache: 'no-store' });
      const text = await res.text();
      const match = text.match(/@version\s+([0-9.]+)/);
      if (match) {
        const latest = match[1];
        if (latest !== PLUGIN_VERSION) showUpdateButton();
      }
    } catch (e) {}
  }

  wireOptions();
  applyPersistedOptionClasses();
  overlayInit();
  checkForUpdate();

  const observer = new MutationObserver(() => {
    overlayInit();
    const btnEl = document.querySelector('#guruToolsBtn');
    if (btnEl && !btnEl._gtBound) {
      btnEl.addEventListener('click', openModal);
      btnEl._gtBound = true;
    }
    if (overlay.style.display !== 'block') overlay.style.display = 'none';
    if (modal.style.display !== 'flex') modal.style.display = 'none';
    applyPersistedOptionClasses();
    const sorterEnabled = load(idFor(1, 7));
    if (sorterEnabled) toggleItemDexSorter(true);
  });

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

  const profileObserver = new MutationObserver(() => {
    const sorterEnabled = load(idFor(1, 7));
    toggleItemDexSorter(sorterEnabled);
  });

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

  document.addEventListener('DOMContentLoaded', () => {
    applyPersistedOptionClasses();
  });

  function scheduleHolidayRecheck() {
    const now = new Date();
    const nextMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0, 0);
    const msUntilMidnight = nextMidnight.getTime() - now.getTime();
    setTimeout(() => {
      updateHolidaySpirit(load(idFor(1, 6)));
      scheduleHolidayRecheck();
    }, Math.max(msUntilMidnight, 60000));
  }

  scheduleHolidayRecheck();
})();