YouTube Layout Plus

Home: collapse guide, 5 videos per row, hide Shorts. Watch: hide recommendations sidebar and center the player/content. Adds a topbar theme switcher that triggers YouTube's official Appearance modes.

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

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

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

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

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

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

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.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         YouTube Layout Plus
// @namespace    https://qazwsx123.uk/
// @version      0.1.14
// @description  Home: collapse guide, 5 videos per row, hide Shorts. Watch: hide recommendations sidebar and center the player/content. Adds a topbar theme switcher that triggers YouTube's official Appearance modes.
// @author       Codex
// @match        https://www.youtube.com/*
// @run-at       document-idle
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  const STYLE_ID = 'tm-youtube-layout-plus-style';
  const THEME_SWITCHER_ID = 'tm-youtube-layout-plus-theme-switcher';
  const THEME_STATUS_ID = 'tm-youtube-layout-plus-theme-status';
  const THEME_BUSY_CLASS = 'tm-youtube-layout-plus-theme-busy';
  const THEME_MODE_STORAGE_KEY = 'tm-youtube-layout-plus-theme-mode';
  const THEME_MODES = [
    { mode: 'auto', label: 'Auto', prefF6: '80', signal: 'TOGGLE_DARK_THEME_DEVICE' },
    { mode: 'light', label: 'Light', prefF6: '80080', signal: 'TOGGLE_DARK_THEME_OFF' },
    { mode: 'dark', label: 'Dark', prefF6: '480', signal: 'TOGGLE_DARK_THEME_ON' },
  ];

  let currentUrl = location.href;
  let applyTimer = null;
  let bodyObserver = null;
  let routeTimer = null;
  let lastAutoCollapsedHomeUrl = '';
  let currentThemeMode = loadStoredThemeMode();
  let themeSwitchInFlight = false;
  let pendingThemeMode = null;

  function isHomePage() {
    return location.pathname === '/';
  }

  function isWatchPage() {
    return location.pathname === '/watch';
  }

  function ensureStyle() {
    if (document.getElementById(STYLE_ID)) return;

    const style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = `
      html.tm-youtube-layout-plus-home {
        --tm-guide-collapsed-width: 72px;
        --tm-chip-overlay-shift: 18px;
        --tm-chip-strip-bg:
          radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.42) 0%, rgba(255, 255, 255, 0) 34%),
          radial-gradient(circle at 82% 6%, rgba(255, 255, 255, 0.22) 0%, rgba(255, 255, 255, 0) 28%),
          linear-gradient(180deg, rgba(214, 223, 233, 0.32) 0%, rgba(191, 201, 212, 0.22) 100%);
        --tm-chip-active-bg:
          radial-gradient(circle at 20% 0%, rgba(255, 255, 255, 0.52) 0%, rgba(255, 255, 255, 0) 42%),
          linear-gradient(180deg, rgba(250, 252, 255, 0.52) 0%, rgba(229, 236, 243, 0.28) 100%);
        --tm-chip-text: #3b424b;
        --tm-chip-text-active: #1f2328;
        --tm-chip-separator: rgba(70, 77, 88, 0.18);
        --tm-chip-strip-border: rgba(255, 255, 255, 0.24);
        --tm-chip-strip-top-line: rgba(156, 164, 175, 0.32);
        --tm-chip-strip-shadow-top: rgba(255, 255, 255, 0.24);
        --tm-chip-strip-shadow-bottom: rgba(43, 50, 59, 0.12);
        --tm-chip-hover-bg: rgba(255, 255, 255, 0.12);
        --tm-chip-active-border: rgba(148, 157, 169, 0.22);
        --tm-chip-active-top-stroke: rgba(148, 157, 169, 0.2);
        --tm-chip-active-shadow-top: rgba(255, 255, 255, 0.4);
        --tm-chip-active-shadow-bottom: rgba(148, 157, 169, 0.24);
        --tm-chip-active-shadow-mid: rgba(60, 64, 67, 0.1);
        --tm-chip-active-shadow-ring: rgba(118, 126, 137, 0.1);
        --tm-chip-arrow-bg:
          radial-gradient(circle at 20% 0%, rgba(255, 255, 255, 0.28) 0%, rgba(255, 255, 255, 0) 38%),
          linear-gradient(180deg, rgba(248, 251, 255, 0.28) 0%, rgba(228, 235, 242, 0.18) 100%);
        --tm-chip-arrow-shadow-top: rgba(255, 255, 255, 0.18);
        --tm-chip-arrow-shadow-mid: rgba(60, 64, 67, 0.08);
        --tm-chip-glass-blur: 34px;
      }

      html[dark].tm-youtube-layout-plus-home {
        --tm-chip-strip-bg:
          radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 34%),
          linear-gradient(180deg, rgba(46, 51, 58, 0.52) 0%, rgba(28, 31, 37, 0.38) 100%);
        --tm-chip-active-bg:
          radial-gradient(circle at 20% 0%, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 40%),
          linear-gradient(180deg, rgba(86, 92, 100, 0.38) 0%, rgba(61, 66, 73, 0.26) 100%);
        --tm-chip-text: rgba(231, 234, 237, 0.9);
        --tm-chip-text-active: #ffffff;
        --tm-chip-separator: rgba(255, 255, 255, 0.1);
        --tm-chip-strip-border: rgba(255, 255, 255, 0.1);
        --tm-chip-strip-top-line: rgba(255, 255, 255, 0.12);
        --tm-chip-strip-shadow-top: rgba(255, 255, 255, 0.08);
        --tm-chip-strip-shadow-bottom: rgba(0, 0, 0, 0.28);
        --tm-chip-hover-bg: rgba(255, 255, 255, 0.08);
        --tm-chip-active-border: rgba(255, 255, 255, 0.12);
        --tm-chip-active-top-stroke: rgba(255, 255, 255, 0.08);
        --tm-chip-active-shadow-top: rgba(255, 255, 255, 0.06);
        --tm-chip-active-shadow-bottom: rgba(255, 255, 255, 0.04);
        --tm-chip-active-shadow-mid: rgba(0, 0, 0, 0.18);
        --tm-chip-active-shadow-ring: rgba(255, 255, 255, 0.06);
        --tm-chip-arrow-bg:
          radial-gradient(circle at 20% 0%, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 38%),
          linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.06) 100%);
        --tm-chip-arrow-shadow-top: rgba(255, 255, 255, 0.06);
        --tm-chip-arrow-shadow-mid: rgba(0, 0, 0, 0.2);
        --tm-chip-glass-blur: 28px;
      }

      html.${THEME_BUSY_CLASS} ytd-popup-container tp-yt-iron-dropdown {
        visibility: hidden !important;
      }

      #${THEME_SWITCHER_ID} {
        position: relative;
        display: flex;
        align-items: center;
        gap: 0;
        margin-left: 12px;
        padding: 4px 6px;
        border: 1px solid var(--yt-spec-10-percent-layer, rgba(0, 0, 0, 0.12));
        border-radius: 20px;
        background: var(--yt-spec-badge-chip-background, rgba(0, 0, 0, 0.05));
      }

      #${THEME_STATUS_ID} {
        position: fixed;
        top: 72px;
        right: 24px;
        z-index: 2200;
        max-width: 320px;
        padding: 10px 14px;
        border: 1px solid rgba(190, 60, 60, 0.28);
        border-radius: 14px;
        background: rgba(126, 28, 28, 0.92);
        color: #fff;
        font: 500 13px/1.4 Roboto, Arial, sans-serif;
        box-shadow: 0 10px 24px rgba(0, 0, 0, 0.24);
        opacity: 0;
        transform: translateY(-6px);
        pointer-events: none;
        transition: opacity 160ms ease, transform 160ms ease;
      }

      #${THEME_STATUS_ID}[data-visible="true"] {
        opacity: 1;
        transform: translateY(0);
      }

      #${THEME_SWITCHER_ID} .tm-theme-switch-track {
        display: inline-flex;
        align-items: center;
        gap: 4px;
      }

      #${THEME_SWITCHER_ID} .tm-theme-switch-option {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 52px;
        height: 28px;
        padding: 0 10px;
        border: 0;
        border-radius: 14px;
        background: transparent;
        color: var(--yt-spec-text-primary, #0f0f0f);
        cursor: pointer;
        font: 600 12px/1 Roboto, Arial, sans-serif;
        transition: background 140ms ease, color 140ms ease, box-shadow 140ms ease;
      }

      #${THEME_SWITCHER_ID} .tm-theme-switch-option:hover {
        background: var(--yt-spec-badge-chip-background-hover, rgba(0, 0, 0, 0.08));
      }

      #${THEME_SWITCHER_ID} .tm-theme-switch-option[data-active="true"] {
        background: var(--yt-spec-brand-background-solid, rgba(15, 15, 15, 0.92));
        color: var(--yt-spec-static-brand-white, #fff);
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
      }

      html[dark] #${THEME_SWITCHER_ID} {
        border-color: rgba(255, 255, 255, 0.14);
        background: rgba(255, 255, 255, 0.04);
      }

      html[dark] #${THEME_SWITCHER_ID} .tm-theme-switch-option {
        color: rgba(255, 255, 255, 0.92);
      }

      html[dark] #${THEME_SWITCHER_ID} .tm-theme-switch-option[data-active="true"] {
        background: rgba(255, 255, 255, 0.14);
        color: #fff;
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.32);
      }

      html.tm-youtube-layout-plus-home ytd-app[mini-guide-visible]:not([guide-persistent-and-visible]) ytd-feed-filter-chip-bar-renderer,
      html.tm-youtube-layout-plus-home ytd-app[mini-guide-visible]:not([guide-persistent-and-visible]) #chips-wrapper {
        left: var(--tm-guide-collapsed-width) !important;
        width: calc(100vw - var(--tm-guide-collapsed-width)) !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer {
        padding: 0 14px !important;
        background: transparent !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #chips-wrapper,
      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #chips {
        align-items: center !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #chips-wrapper {
        transform: translateY(var(--tm-chip-overlay-shift)) !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #chips-content {
        position: relative !important;
        overflow: hidden !important;
        isolation: isolate !important;
        padding: 5px 8px !important;
        margin-top: 0 !important;
        border: 1px solid var(--tm-chip-strip-border) !important;
        border-radius: 22px !important;
        background: var(--tm-chip-strip-bg) !important;
        backdrop-filter: blur(var(--tm-chip-glass-blur)) saturate(220%) contrast(1.05) brightness(1.08) !important;
        -webkit-backdrop-filter: blur(var(--tm-chip-glass-blur)) saturate(220%) contrast(1.05) brightness(1.08) !important;
        box-shadow:
          inset 0 1px 0 var(--tm-chip-strip-top-line),
          inset 0 1px 0 var(--tm-chip-strip-shadow-top),
          0 2px 0 rgba(34, 40, 49, 0.11),
          0 3px 2px rgba(34, 40, 49, 0.06),
          0 5px 7px rgba(34, 40, 49, 0.03) !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #chips-content::before {
        content: '' !important;
        position: absolute !important;
        inset: 0 !important;
        border-radius: inherit !important;
        background:
          linear-gradient(180deg, rgba(255, 255, 255, 0.26) 0%, rgba(255, 255, 255, 0.08) 36%, rgba(255, 255, 255, 0.02) 100%) !important;
        pointer-events: none !important;
        z-index: 0 !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #chips-content::after {
        content: '' !important;
        position: absolute !important;
        inset: 0 !important;
        border-radius: inherit !important;
        background:
          radial-gradient(circle at 50% 100%, rgba(180, 198, 220, 0.12) 0%, rgba(180, 198, 220, 0) 58%) !important;
        pointer-events: none !important;
        z-index: 0 !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer {
        position: relative !important;
        z-index: 1 !important;
        margin: 0 !important;
        padding: 0 2px !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer + yt-chip-cloud-chip-renderer::before {
        content: '' !important;
        position: absolute !important;
        left: -1px !important;
        top: 9px !important;
        bottom: 9px !important;
        width: 1px !important;
        border-radius: 999px !important;
        background: var(--tm-chip-separator) !important;
        pointer-events: none !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer button.ytChipShapeButtonReset {
        border-radius: 18px !important;
        overflow: visible !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer .ytChipShapeChip {
        position: relative !important;
        min-height: 38px !important;
        padding: 0 22px !important;
        border: 0 !important;
        border-radius: 18px !important;
        background: transparent !important;
        box-shadow: none !important;
        color: var(--tm-chip-text) !important;
        font-weight: 600 !important;
        letter-spacing: -0.01em !important;
        transition:
          background 140ms ease,
          box-shadow 140ms ease,
          color 140ms ease,
          transform 140ms ease,
          backdrop-filter 140ms ease !important;
        backdrop-filter: blur(calc(var(--tm-chip-glass-blur) * 0.7)) saturate(170%) brightness(1.04) !important;
        -webkit-backdrop-filter: blur(calc(var(--tm-chip-glass-blur) * 0.7)) saturate(170%) brightness(1.04) !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer .ytChipShapeChip > div {
        font-size: 15px !important;
        line-height: 36px !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer:hover .ytChipShapeChip,
      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer button:focus-visible .ytChipShapeChip {
        background: var(--tm-chip-hover-bg) !important;
        color: var(--tm-chip-text-active) !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer[selected] .ytChipShapeChip,
      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer .ytChipShapeChip.ytChipShapeActive {
        background: var(--tm-chip-active-bg) !important;
        border: 1px solid var(--tm-chip-active-border) !important;
        box-shadow: none !important;
        color: var(--tm-chip-text-active) !important;
        transform: none !important;
        z-index: 1 !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer[selected]::before,
      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer[selected] + yt-chip-cloud-chip-renderer::before {
        opacity: 0 !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer yt-touch-feedback-shape,
      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer .yt-spec-touch-feedback-shape__stroke,
      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer yt-chip-cloud-chip-renderer .yt-spec-touch-feedback-shape__fill {
        border-radius: 18px !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #right-arrow.ytd-feed-filter-chip-bar-renderer,
      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #left-arrow.ytd-feed-filter-chip-bar-renderer {
        margin-top: 0 !important;
        transform: none !important;
      }

      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #right-arrow button,
      html.tm-youtube-layout-plus-home ytd-feed-filter-chip-bar-renderer #left-arrow button {
        border: 0 !important;
        border-radius: 18px !important;
        background: var(--tm-chip-arrow-bg) !important;
        transform: none !important;
        backdrop-filter: blur(calc(var(--tm-chip-glass-blur) * 0.72)) saturate(175%) brightness(1.04) !important;
        -webkit-backdrop-filter: blur(calc(var(--tm-chip-glass-blur) * 0.72)) saturate(175%) brightness(1.04) !important;
        box-shadow:
          inset 0 1px 0 var(--tm-chip-arrow-shadow-top),
          0 4px 12px var(--tm-chip-arrow-shadow-mid) !important;
      }

      html.tm-youtube-layout-plus-home ytd-app[mini-guide-visible]:not([guide-persistent-and-visible]) ytd-page-manager {
        margin-left: var(--tm-guide-collapsed-width) !important;
        width: calc(100% - var(--tm-guide-collapsed-width)) !important;
      }

      html.tm-youtube-layout-plus-home ytd-browse {
        overflow: visible !important;
      }

      html.tm-youtube-layout-plus-home ytd-app[mini-guide-visible]:not([guide-persistent-and-visible]) ytd-two-column-browse-results-renderer,
      html.tm-youtube-layout-plus-home ytd-app[mini-guide-visible]:not([guide-persistent-and-visible]) ytd-rich-grid-renderer,
      html.tm-youtube-layout-plus-home ytd-app[mini-guide-visible]:not([guide-persistent-and-visible]) #contents.ytd-rich-grid-renderer {
        margin-left: 0 !important;
        width: auto !important;
      }

      html.tm-youtube-layout-plus-home #contents.ytd-rich-grid-renderer {
        margin-top: 0 !important;
        padding-top: calc(var(--tm-chip-overlay-shift) + 8px) !important;
        padding-left: 14px !important;
        padding-right: 12px !important;
        box-sizing: border-box !important;
      }

      html.tm-youtube-layout-plus-home ytd-rich-grid-renderer {
        --ytd-rich-grid-items-per-row: 5 !important;
        --ytd-rich-grid-posts-per-row: 5 !important;
      }

      html.tm-youtube-layout-plus-home ytd-rich-item-renderer,
      html.tm-youtube-layout-plus-home ytd-rich-grid-row,
      html.tm-youtube-layout-plus-home ytd-rich-grid-media,
      html.tm-youtube-layout-plus-home #contents.ytd-rich-grid-renderer > * {
        max-width: none !important;
      }

      html.tm-youtube-layout-plus-home ytd-rich-item-renderer {
        position: relative !important;
        z-index: 0 !important;
        margin-left: 0 !important;
        margin-right: 10px !important;
        transform-origin: center top !important;
        transition:
          transform 160ms ease,
          box-shadow 160ms ease,
          z-index 160ms ease !important;
        will-change: transform !important;
      }

      html.tm-youtube-layout-plus-home ytd-rich-item-renderer:hover,
      html.tm-youtube-layout-plus-home ytd-rich-item-renderer:focus-within {
        transform: scale(1.1) !important;
        z-index: 4 !important;
      }

      html.tm-youtube-layout-plus-home ytd-rich-section-renderer,
      html.tm-youtube-layout-plus-home ytd-rich-shelf-renderer,
      html.tm-youtube-layout-plus-home ytd-reel-shelf-renderer {
        display: none !important;
      }

      html.tm-youtube-layout-plus-watch ytd-watch-flexy #secondary,
      html.tm-youtube-layout-plus-watch ytd-watch-flexy #related,
      html.tm-youtube-layout-plus-watch ytd-watch-flexy #secondary-inner,
      html.tm-youtube-layout-plus-watch ytd-watch-next-secondary-results-renderer {
        display: none !important;
      }

      html.tm-youtube-layout-plus-watch ytd-watch-flexy[is-two-columns_] #primary {
        max-width: none !important;
        width: min(1400px, calc(100vw - 48px)) !important;
        margin: 0 auto !important;
      }

      html.tm-youtube-layout-plus-watch ytd-watch-flexy[is-two-columns_] #columns {
        display: block !important;
      }

      html.tm-youtube-layout-plus-watch ytd-watch-flexy[is-two-columns_] #primary-inner,
      html.tm-youtube-layout-plus-watch ytd-watch-flexy[is-two-columns_] #above-the-fold,
      html.tm-youtube-layout-plus-watch ytd-watch-flexy[is-two-columns_] #below {
        max-width: none !important;
      }
    `;

    document.head.appendChild(style);
  }

  function collapseGuide() {
    const app = document.querySelector('ytd-app');
    if (app) {
      app.removeAttribute('guide-persistent-and-visible');
      app.setAttribute('mini-guide-visible', '');
    }

    const drawer = document.querySelector('tp-yt-app-drawer#guide');
    if (drawer) {
      drawer.removeAttribute('opened');
      drawer.style.width = '';
      drawer.style.minWidth = '';
      drawer.style.visibility = '';
    }

    const guideRenderer = document.querySelector('ytd-guide-renderer');
    if (guideRenderer) {
      guideRenderer.style.display = '';
      guideRenderer.style.width = '';
      guideRenderer.style.minWidth = '';
    }
  }

  function applyHomeLayout() {
    const richGrid = document.querySelector('ytd-rich-grid-renderer');
    if (!richGrid) return;

    document.documentElement.classList.add('tm-youtube-layout-plus-home');

    if (lastAutoCollapsedHomeUrl !== location.href) {
      collapseGuide();
      lastAutoCollapsedHomeUrl = location.href;
    }
  }

  function applyWatchLayout() {
    const watchFlexy = document.querySelector('ytd-watch-flexy');
    if (!watchFlexy) return;

    document.documentElement.classList.add('tm-youtube-layout-plus-watch');
  }

  function clearModeClasses() {
    document.documentElement.classList.remove(
      'tm-youtube-layout-plus-home',
      'tm-youtube-layout-plus-watch'
    );
  }

  function loadStoredThemeMode() {
    try {
      const mode = window.localStorage.getItem(THEME_MODE_STORAGE_KEY);
      return THEME_MODES.some((item) => item.mode === mode) ? mode : null;
    } catch {
      return null;
    }
  }

  function persistThemeMode(mode) {
    try {
      if (THEME_MODES.some((item) => item.mode === mode)) {
        window.localStorage.setItem(THEME_MODE_STORAGE_KEY, mode);
      } else {
        window.localStorage.removeItem(THEME_MODE_STORAGE_KEY);
      }
    } catch {
      // Ignore storage failures.
    }
  }

  function delay(ms) {
    return new Promise((resolve) => {
      window.setTimeout(resolve, ms);
    });
  }

  function getThemePrefValue(mode) {
    return THEME_MODES.find((item) => item.mode === mode)?.prefF6 || null;
  }

  function getThemeSignal(mode) {
    return THEME_MODES.find((item) => item.mode === mode)?.signal || null;
  }

  function getThemeOptionSignal(option) {
    return option?.data?.serviceEndpoint?.signalServiceEndpoint?.actions?.[0]?.signalAction?.signal || null;
  }

  function getThemeModeFromPrefCookie() {
    const prefEntries = document.cookie
      .split('; ')
      .filter((entry) => entry.startsWith('PREF='));
    const lastEntry = prefEntries[prefEntries.length - 1];
    if (!lastEntry) return null;

    try {
      const pref = decodeURIComponent(lastEntry.slice(5));
      const f6Match = pref.match(/(?:^|&)f6=(\d+)/);
      if (!f6Match) return null;

      const f6Value = f6Match[1];
      if (/80080$/.test(f6Value)) return 'light';
      if (/480$/.test(f6Value)) return 'dark';
      if (/80$/.test(f6Value)) return 'auto';
      return 'auto';
    } catch {
      return null;
    }
  }

  function inferThemeModeFromDocument() {
    return document.documentElement.hasAttribute('dark') ? 'dark' : 'light';
  }

  function getObservedThemeMode() {
    return getThemeModeFromPrefCookie()
      || detectThemeModeFromOptions()
      || inferThemeModeFromDocument();
  }

  function showThemeStatusMessage(message) {
    let status = document.getElementById(THEME_STATUS_ID);
    if (!status) {
      status = document.createElement('div');
      status.id = THEME_STATUS_ID;
      status.dataset.visible = 'false';
      document.body.appendChild(status);
    }

    status.textContent = message;
    status.dataset.visible = 'true';
    window.clearTimeout(showThemeStatusMessage.hideTimer);
    showThemeStatusMessage.hideTimer = window.setTimeout(() => {
      status.dataset.visible = 'false';
    }, 2600);
  }

  function hideThemeStatusMessage() {
    const status = document.getElementById(THEME_STATUS_ID);
    if (!status) return;
    status.dataset.visible = 'false';
    window.clearTimeout(showThemeStatusMessage.hideTimer);
  }

  function isVisibleElement(element) {
    if (!element) return false;
    const rect = element.getBoundingClientRect();
    return rect.width > 0 && rect.height > 0;
  }

  function getOfficialThemeOptions(options = {}) {
    const { visibleOnly = false } = options;
    return Array.from(document.querySelectorAll('ytd-compact-link-renderer')).filter(
      (option) => THEME_MODES.some((mode) => getThemeOptionSignal(option) === mode.signal)
        && (!visibleOnly || isVisibleElement(option))
    );
  }

  function detectThemeModeFromOptions(options = getOfficialThemeOptions({ visibleOnly: true })) {
    const selectedOption = options.find((option) => option?.data?.icon?.iconType === 'CHECK');
    if (!selectedOption) return null;
    return THEME_MODES.find((mode) => mode.signal === getThemeOptionSignal(selectedOption))?.mode || null;
  }

  function findAccountMenuButton() {
    return document.querySelector('button#avatar-btn')
      || Array.from(document.querySelectorAll('ytd-topbar-menu-button-renderer button, button')).find((button) => {
        if (button.id === 'avatar-btn') return true;
        if (button.querySelector('img') && button.getAttribute('aria-haspopup') === 'true') return true;
        return false;
      })
      || null;
  }

  function findAppearanceMenuEntry() {
    return Array.from(document.querySelectorAll('ytd-toggle-theme-compact-link-renderer')).find(
      (entry) => isVisibleElement(entry)
    ) || null;
  }

  function closeOfficialThemeMenus() {
    document.dispatchEvent(
      new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true })
    );
    document.dispatchEvent(
      new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true })
    );
  }

  async function waitFor(getValue, timeout = 2200) {
    const startedAt = Date.now();
    while (Date.now() - startedAt < timeout) {
      const value = getValue();
      if (value) return value;
      await delay(50);
    }
    return null;
  }

  async function ensureOfficialThemeOptionsVisible() {
    const existingOptions = getOfficialThemeOptions({ visibleOnly: true });
    if (existingOptions.length) return existingOptions;

    const existingAppearanceEntry = findAppearanceMenuEntry();
    if (existingAppearanceEntry) {
      existingAppearanceEntry.click();
      return await waitFor(() => {
        const options = getOfficialThemeOptions({ visibleOnly: true });
        return options.length ? options : null;
      });
    }

    closeOfficialThemeMenus();
    await delay(80);

    const accountMenuButton = findAccountMenuButton();
    if (!accountMenuButton) return null;

    accountMenuButton.click();
    const appearanceEntry = await waitFor(findAppearanceMenuEntry);
    if (!appearanceEntry) return null;

    appearanceEntry.click();
    return await waitFor(() => {
      const options = getOfficialThemeOptions({ visibleOnly: true });
      return options.length ? options : null;
    });
  }

  function updateThemeSwitcherUI() {
    const switcher = document.getElementById(THEME_SWITCHER_ID);
    if (!switcher) return;

    if (!pendingThemeMode) {
      const observedMode = getObservedThemeMode();
      if (observedMode) {
        currentThemeMode = observedMode;
        persistThemeMode(observedMode);
      }
    } else if (!currentThemeMode) {
      currentThemeMode = loadStoredThemeMode()
        || inferThemeModeFromDocument();
    }

    const visibleMode = pendingThemeMode || currentThemeMode;

    const optionButtons = switcher.querySelectorAll('.tm-theme-switch-option[data-mode]');
    for (const button of optionButtons) {
      const isActive = button.dataset.mode === visibleMode;
      button.dataset.active = String(isActive);
      button.setAttribute('aria-pressed', String(isActive));
      button.disabled = themeSwitchInFlight;
    }
  }

  async function verifyThemeMode(mode) {
    const expectedDark = mode === 'dark'
      || (mode === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
    let lastResult = null;

    for (let attempt = 0; attempt < 3; attempt += 1) {
      const options = await ensureOfficialThemeOptionsVisible();
      const menuMode = options?.length ? detectThemeModeFromOptions(options) : null;
      const cookieMode = getThemeModeFromPrefCookie();
      const hasDark = document.documentElement.hasAttribute('dark');
      const actualMode = menuMode || cookieMode || inferThemeModeFromDocument();
      const menuConsistent = !menuMode || menuMode === mode;
      const cookieConsistent = !cookieMode || cookieMode === mode;
      const darkConsistent = hasDark === expectedDark;

      lastResult = {
        ok: menuConsistent && cookieConsistent && darkConsistent,
        actualMode,
        menuMode,
        cookieMode,
        hasDark,
      };

      if (lastResult.ok) {
        closeOfficialThemeMenus();
        return lastResult;
      }

      closeOfficialThemeMenus();
      await delay(180);
    }

    return lastResult || {
      ok: false,
      actualMode: getObservedThemeMode(),
      menuMode: null,
      cookieMode: getThemeModeFromPrefCookie(),
      hasDark: document.documentElement.hasAttribute('dark'),
    };
  }

  async function setThemeMode(mode) {
    if (themeSwitchInFlight) return;
    if (!THEME_MODES.some((item) => item.mode === mode)) return;

    const previousMode = currentThemeMode
      || loadStoredThemeMode()
      || getThemeModeFromPrefCookie()
      || detectThemeModeFromOptions()
      || (document.documentElement.hasAttribute('dark') ? 'dark' : 'light');
    if (previousMode === mode) {
      currentThemeMode = mode;
      persistThemeMode(mode);
      pendingThemeMode = null;
      updateThemeSwitcherUI();
      return;
    }

    themeSwitchInFlight = true;
    document.documentElement.classList.add(THEME_BUSY_CLASS);
    pendingThemeMode = mode;

    try {
      updateThemeSwitcherUI();

      const options = await ensureOfficialThemeOptionsVisible();
      if (!options?.length) {
        currentThemeMode = previousMode;
        persistThemeMode(previousMode);
        updateThemeSwitcherUI();
        return;
      }

      const visibleMode = detectThemeModeFromOptions(options);
      let didRequestOfficialMode = visibleMode === mode;
      if (visibleMode !== mode) {
        const targetOption = options.find((option) => getThemeOptionSignal(option) === getThemeSignal(mode));
        if (!targetOption) {
          currentThemeMode = previousMode;
          persistThemeMode(previousMode);
          updateThemeSwitcherUI();
          return;
        }

        targetOption.click();
        didRequestOfficialMode = true;
      }

      // Once the official menu action is triggered, trust the requested mode for the switcher UI.
      currentThemeMode = mode;
      persistThemeMode(mode);
      updateThemeSwitcherUI();

      const verification = await verifyThemeMode(mode);
      if (!verification.ok) {
        currentThemeMode = verification.actualMode || previousMode;
        persistThemeMode(currentThemeMode);
        pendingThemeMode = null;
        updateThemeSwitcherUI();
        showThemeStatusMessage(
          `Theme switch failed: expected ${mode}, actual ${currentThemeMode || 'unknown'}.`
        );
        return;
      }

      currentThemeMode = verification.actualMode || mode;
      persistThemeMode(currentThemeMode);
      hideThemeStatusMessage();
    } finally {
      closeOfficialThemeMenus();
      themeSwitchInFlight = false;
      document.documentElement.classList.remove(THEME_BUSY_CLASS);
      pendingThemeMode = null;
      currentThemeMode = currentThemeMode || loadStoredThemeMode() || getThemeModeFromPrefCookie() || previousMode;
      updateThemeSwitcherUI();
    }
  }

  function ensureThemeSwitcher() {
    const logoRenderer = document.querySelector('ytd-topbar-logo-renderer');
    if (!logoRenderer) return;

    let switcher = document.getElementById(THEME_SWITCHER_ID);
    if (!switcher) {
      const track = document.createElement('div');
      track.className = 'tm-theme-switch-track';
      track.setAttribute('role', 'group');
      track.setAttribute('aria-label', 'Theme switcher');

      switcher = document.createElement('div');
      switcher.id = THEME_SWITCHER_ID;

      for (const item of THEME_MODES) {
        const optionButton = document.createElement('button');
        optionButton.type = 'button';
        optionButton.className = 'tm-theme-switch-option';
        optionButton.dataset.mode = item.mode;
        optionButton.dataset.active = 'false';
        optionButton.textContent = item.label;
        optionButton.setAttribute('aria-pressed', 'false');
        optionButton.addEventListener('click', async (event) => {
          event.preventDefault();
          event.stopPropagation();
          await setThemeMode(item.mode);
        });
        track.appendChild(optionButton);
      }

      switcher.append(track);

      logoRenderer.insertAdjacentElement('afterend', switcher);
    } else if (switcher.previousElementSibling !== logoRenderer) {
      logoRenderer.insertAdjacentElement('afterend', switcher);
    }

    updateThemeSwitcherUI();
  }

  function applyLayout() {
    ensureStyle();
    clearModeClasses();

    try {
      ensureThemeSwitcher();
    } catch (error) {
      console.error('YouTube Layout Plus theme switcher failed:', error);
    }

    if (isHomePage()) {
      applyHomeLayout();
      return;
    }

    if (isWatchPage()) {
      applyWatchLayout();
    }
  }

  function scheduleApply() {
    clearTimeout(applyTimer);
    applyTimer = window.setTimeout(applyLayout, 120);
  }

  function startObservers() {
    if (bodyObserver) return;

    bodyObserver = new MutationObserver(() => {
      scheduleApply();
    });

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

  function watchRoute() {
    if (routeTimer) return;

    routeTimer = window.setInterval(() => {
      if (location.href === currentUrl) return;
      currentUrl = location.href;
      if (!isHomePage()) {
        lastAutoCollapsedHomeUrl = '';
      }
      scheduleApply();
    }, 500);
  }

  function init() {
    ensureStyle();
    scheduleApply();
    startObservers();
    watchRoute();
  }

  init();
})();