Google Discover Toggle

Enable/disable Google Discover by toggling ?gl=nz. Adds a non-blocking prompt for first-time users with snooze/never options. Persists across sessions.

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

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

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         Google Discover Toggle
// @namespace    https://github.com/ymhomer/ym_Userscript
// @version      1.3
// @description  Enable/disable Google Discover by toggling ?gl=nz. Adds a non-blocking prompt for first-time users with snooze/never options. Persists across sessions.
// @author       ymhomer
// @match        https://www.google.com/*
// @match        https://www.google.*/*
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-start
// @noframes
// @license      MIT
// ==/UserScript==

;(async function () {
  'use strict';

  const STORAGE_KEY = 'discoverEnabled';
  const PROMPT_NEVER_KEY = 'discoverPromptNever';
  const SNOOZE_UNTIL_KEY = 'discoverSnoozeUntil';
  const GL_PARAM = 'gl';
  const DISCOVER_GL = 'nz';
  const SNOOZE_MS = 24 * 60 * 60 * 1000; // 1 day
  const REDIRECT_GUARD_KEY = 'gdt_last_redirect';
  const REDIRECT_GUARD_MS = 8000;

  const GMAPI = (typeof GM?.getValue === 'function')
    ? {
        get: (k, d) => GM.getValue(k, d),
        set: (k, v) => GM.setValue(k, v),
        menu: (label, cb) => GM.registerMenuCommand(label, cb),
      }
    : {
        get: (k, d) => Promise.resolve(GM_getValue(k, d)),
        set: (k, v) => Promise.resolve(GM_setValue(k, v)),
        menu: (label, cb) => GM_registerMenuCommand(label, cb),
      };

  const topWindow = (window.top === window.self);
  if (!topWindow) return;

  const path = location.pathname;
  const isEligiblePath = (path === '/' || path === '/search');

  function getGL(urlLike) {
    try {
      const u = new URL(urlLike || location.href);
      return u.searchParams.get(GL_PARAM);
    } catch {
      return null;
    }
  }

  function withGL(urlLike, value) {
    try {
      const u = new URL(urlLike || location.href);
      const sp = u.searchParams;
      if (value == null) {
        sp.delete(GL_PARAM);
      } else {
        sp.set(GL_PARAM, value);
      }
      return u.toString();
    } catch {
      return urlLike || location.href;
    }
  }

  function shouldGuardRedirect() {
    try {
      const last = sessionStorage.getItem(REDIRECT_GUARD_KEY);
      const now = Date.now();
      if (last && (now - Number(last) < REDIRECT_GUARD_MS)) return true;
      sessionStorage.setItem(REDIRECT_GUARD_KEY, String(now));
      return false;
    } catch {
      return false;
    }
  }

  // Persistent values
  const enabled = await GMAPI.get(STORAGE_KEY, undefined);
  const promptNever = await GMAPI.get(PROMPT_NEVER_KEY, false);
  const snoozeUntil = Number(await GMAPI.get(SNOOZE_UNTIL_KEY, 0)) || 0;

  // If user already enabled, enforce gl=nz if needed
  if (enabled === true) {
    const currentGL = getGL(location.href);
    if (currentGL !== DISCOVER_GL && !shouldGuardRedirect()) {
      location.replace(withGL(location.href, DISCOVER_GL));
      return;
    }
  }

  // Menu toggle
  const menuLabel = (() => {
    if (enabled === true) return 'Disable Google Discover';
    if (enabled === false) return 'Enable Google Discover';
    return 'Enable Google Discover (not set)';
  })();

  GMAPI.menu(menuLabel, async () => {
    const newState = !(enabled === true);
    await GMAPI.set(STORAGE_KEY, newState);
    if (newState) {
      if (getGL(location.href) !== DISCOVER_GL) {
        location.replace(withGL(location.href, DISCOVER_GL));
        return;
      }
    }
    // if disabling, do not touch current gl — just reload to apply menu label if needed
    location.reload();
  });

  // First-time prompt logic (only if no decision yet)
  const shouldPrompt =
    enabled === undefined &&
    !promptNever &&
    Date.now() >= snoozeUntil &&
    isEligiblePath;

  if (!shouldPrompt) return;

  // Wait for body to exist before injecting UI
  if (!document.body) {
    await new Promise((res) => {
      const obs = new MutationObserver(() => {
        if (document.body) {
          obs.disconnect();
          res();
        }
      });
      obs.observe(document.documentElement, { childList: true, subtree: true });
    });
  }

  // Render prompt (Shadow DOM)
  const host = document.createElement('div');
  host.setAttribute('data-gdt', 'host');
  host.style.position = 'fixed';
  host.style.top = '16px';
  host.style.right = '16px';
  host.style.zIndex = '2147483647';
  host.style.all = 'initial'; // avoid leaking styles
  document.body.appendChild(host);

  const shadow = host.attachShadow({ mode: 'closed' });
  const wrap = document.createElement('div');

  const style = document.createElement('style');
  style.textContent = `
    :host, * { box-sizing: border-box; }
    .card {
      font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
      font-size: 13px;
      line-height: 1.35;
      color: #1f1f1f;
      background: rgba(255,255,255,0.96);
      backdrop-filter: saturate(150%) blur(6px);
      border: 1px solid rgba(0,0,0,0.08);
      border-radius: 12px;
      box-shadow: 0 8px 30px rgba(0,0,0,0.12);
      width: 280px;
      padding: 12px 12px 10px;
    }
    @media (prefers-color-scheme: dark) {
      .card {
        color: #eaeaea;
        background: rgba(32,32,32,0.92);
        border-color: rgba(255,255,255,0.08);
        box-shadow: 0 8px 30px rgba(0,0,0,0.48);
      }
      .btn { color: #eaeaea; border-color: rgba(255,255,255,0.18); }
      .btn--primary { background: #1a73e8; color: white; border-color: transparent; }
    }
    .title { font-weight: 600; margin-bottom: 6px; }
    .desc  { opacity: 0.8; margin-bottom: 10px; }
    .row {
      display: flex; gap: 8px; justify-content: flex-end;
    }
    .btn {
      appearance: none;
      border: 1px solid rgba(0,0,0,0.18);
      background: transparent;
      border-radius: 8px;
      padding: 6px 10px;
      cursor: pointer;
      font: inherit;
      transition: transform .06s ease, background .12s ease;
    }
    .btn:hover { background: rgba(0,0,0,0.04); }
    .btn:active { transform: translateY(1px); }
    .btn--primary {
      background: #1a73e8;
      color: white;
      border-color: transparent;
    }
    .sr { position:absolute; width:1px; height:1px; clip:rect(1px,1px,1px,1px); overflow:hidden; white-space:nowrap; }
  `;

  wrap.innerHTML = `
    <div class="card" role="dialog" aria-label="Google Discover setting">
      <div class="title">Enable Google Discover?</div>
      <div class="desc">Add <code>?gl=${DISCOVER_GL}</code> to keep Discover feed consistent on this device.</div>
      <div class="row">
        <button class="btn" data-action="never" aria-label="Never show again">Never</button>
        <button class="btn" data-action="later" aria-label="Not now">Not now</button>
        <button class="btn btn--primary" data-action="enable" aria-label="Enable">Enable</button>
      </div>
      <span class="sr">Use Tab to navigate buttons</span>
    </div>
  `;

  shadow.appendChild(style);
  shadow.appendChild(wrap);

  function removePrompt() {
    try { host.remove(); } catch {}
  }

  async function onAction(action) {
    if (action === 'enable') {
      await GMAPI.set(STORAGE_KEY, true);
      removePrompt();
      if (getGL(location.href) !== DISCOVER_GL) {
        location.replace(withGL(location.href, DISCOVER_GL));
        return;
      }
      location.reload();
    } else if (action === 'later') {
      await GMAPI.set(SNOOZE_UNTIL_KEY, Date.now() + SNOOZE_MS);
      removePrompt();
    } else if (action === 'never') {
      await GMAPI.set(PROMPT_NEVER_KEY, true);
      await GMAPI.set(STORAGE_KEY, false);
      removePrompt();
    }
  }

  wrap.addEventListener('click', (e) => {
    const t = e.target;
    if (!(t instanceof Element)) return;
    const act = t.getAttribute('data-action');
    if (act) onAction(act);
  });

})();