YouTube Subscription List View

Restore YouTube Subscription List View

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         YouTube Subscription List View
// @namespace    xyz.flaflo.yslv
// @version      2.0.1
// @author       Flaflo
// @license      MIT
// @homepageURL  https://github.com/Flaflo/yslv-userscript
// @icon         https://flaflo.xyz/yslv/icon-128.png
// @icon64       https://flaflo.xyz/yslv/icon-64.png
// @description  Restore YouTube Subscription List View
// @match        https://www.youtube.com/*
// @run-at       document-end
// @grant        none
// ==/UserScript==

(function() {
  "use strict";
  const CFG = {
    storageKey: "yslv",
    defaultView: "grid",
    // or "list"
    toggleMountSelector: 'ytd-browse[page-subtype="subscriptions"] ytd-shelf-renderer .grid-subheader #title-container #subscribe-button,ytd-two-column-browse-results-renderer[page-subtype="subscriptions"] ytd-shelf-renderer .grid-subheader #title-container #subscribe-button',
    descStore: {
      key: "yslv_desc_cache_v1",
      ttlMs: 60 * 60 * 1e3,
      maxEntries: 1200,
      saveDebounceMs: 250
    },
    list: {
      maxWidth: 1320,
      // or "90%"
      rowPadY: 20,
      separator: true,
      thumbW: 240,
      thumbRadius: 14,
      shorts: {
        enabled: true,
        cardW: 170
      },
      titleClamp: 2,
      descClamp: 2,
      rowHead: {
        enabled: true,
        gap: 12,
        marginBottom: 20,
        avatarSize: 32
      },
      metaRow: {
        gap: 8
      },
      desc: {
        marginTop: 10,
        skeleton: {
          enabled: true,
          lines: 2,
          lineGap: 6,
          lineHeights: [12, 12, 12],
          lineWidthsPct: [82, 74, 58],
          radius: 9,
          maxW: 520,
          animMs: 5e3
        }
      },
      descFetch: {
        enabled: true,
        maxTotalFetchesPerNav: 60,
        maxConcurrent: 1,
        sentenceCount: 2,
        maxChars: 260
      }
    },
    perf: {
      maxItemsPerTick: 60,
      descQueueIntervalMs: 350
    },
    ids: {
      style: "yslv-subs-style",
      toggle: "yslv-subs-toggle"
    },
    cls: {
      rowHead: "yslv-subs-rowhead",
      rowHeadName: "yslv-subs-rowhead-name",
      metaRow: "yslv-subs-mrow",
      metaCh: "yslv-subs-mch",
      metaRt: "yslv-subs-mrt",
      desc: "yslv-subs-desc",
      descSkel: "yslv-subs-desc-skel",
      btn: "yslv-btn",
      btnIcon: "yslv-btn-ic",
      isShort: "yslv-is-short"
    },
    attr: {
      view: "data-yslv-subs-view"
    },
    cssVars: {
      shimmerX: "--yslvSkelX",
      shortW: "--yslvShortW"
    }
  };
  function createState() {
    return {
      active: false,
      view: "grid",
      styleEl: null,
      q: [],
      qSet: /* @__PURE__ */ new Set(),
      processing: false,
      processedItems: /* @__PURE__ */ new WeakSet(),
      movedAvatars: /* @__PURE__ */ new WeakMap(),
      movedMetaAnchors: /* @__PURE__ */ new WeakMap(),
      mo: null,
      observedTarget: null,
      pmMo: null,
      descCache: /* @__PURE__ */ new Map(),
      descInFlight: /* @__PURE__ */ new Map(),
      descFetches: 0,
      descActive: 0,
      descQueue: [],
      descQueued: /* @__PURE__ */ new Set(),
      descTimer: 0,
      descPumpRunning: false,
      lastQueueSig: "",
      lastPageSig: ""
    };
  }
  function createShimmerState() {
    return {
      raf: 0,
      running: false,
      t0: 0
    };
  }
  function createDescStoreState() {
    return {
      obj: null,
      dirty: false,
      saveT: 0
    };
  }
  function isViewMode(v) {
    return v === "grid" || v === "list";
  }
  function loadView(cfg) {
    try {
      const v = localStorage.getItem(cfg.storageKey);
      return isViewMode(v) ? v : cfg.defaultView;
    } catch {
      return cfg.defaultView;
    }
  }
  function saveView(cfg, v) {
    try {
      localStorage.setItem(cfg.storageKey, v);
    } catch {
    }
  }
  function applyViewAttr(cfg, v) {
    document.documentElement.setAttribute(cfg.attr.view, v);
  }
  function clearViewAttr(cfg) {
    document.documentElement.removeAttribute(cfg.attr.view);
  }
  function isSubsPage() {
    return location.pathname === "/feed/subscriptions";
  }
  function getActiveSubsBrowse() {
    return document.querySelector('ytd-page-manager ytd-browse[page-subtype="subscriptions"]:not([hidden])') || document.querySelector('ytd-browse[page-subtype="subscriptions"]:not([hidden])') || null;
  }
  function getActiveSubsRoot() {
    const b = getActiveSubsBrowse();
    if (!b) return null;
    return b.querySelector("ytd-rich-grid-renderer #contents") || b.querySelector("ytd-rich-grid-renderer") || b;
  }
  function getActiveSubsDoc() {
    return getActiveSubsBrowse() || document;
  }
  function pageSig() {
    return `${location.pathname}|${location.search}|${document.querySelector("ytd-page-manager") ? "pm" : "nopm"}`;
  }
  function nowMs() {
    return Date.now();
  }
  function ensureDescStoreLoaded(cfg, st) {
    if (st.obj) return;
    let raw;
    try {
      raw = localStorage.getItem(cfg.descStore.key) || "";
    } catch {
      raw = "";
    }
    let obj = {};
    if (raw) {
      try {
        const parsed = JSON.parse(raw);
        if (parsed && typeof parsed === "object") obj = parsed;
      } catch {
        obj = {};
      }
    }
    st.obj = obj;
    pruneDescStore(cfg, st);
  }
  function scheduleDescStoreSave(cfg, st) {
    if (st.saveT) return;
    st.saveT = window.setTimeout(() => {
      st.saveT = 0;
      if (!st.dirty) return;
      st.dirty = false;
      try {
        localStorage.setItem(cfg.descStore.key, JSON.stringify(st.obj || {}));
      } catch {
      }
    }, Math.max(0, Number(cfg.descStore.saveDebounceMs) || 250));
  }
  function pruneDescStore(cfg, st) {
    ensureDescStoreLoaded(cfg, st);
    const ttl = Math.max(1, Number(cfg.descStore.ttlMs) || 36e5);
    const maxEntries = Math.max(50, Number(cfg.descStore.maxEntries) || 1200);
    const tNow = nowMs();
    const obj = st.obj || {};
    const entries = [];
    for (const k of Object.keys(obj)) {
      const e = obj[k];
      const t = Number(e?.t || 0);
      if (!t || tNow - t >= ttl) {
        delete obj[k];
        st.dirty = true;
        continue;
      }
      entries.push([k, t]);
    }
    if (entries.length > maxEntries) {
      entries.sort((a, b) => a[1] - b[1]);
      const drop = entries.length - maxEntries;
      for (let i = 0; i < drop; i++) {
        delete obj[entries[i][0]];
        st.dirty = true;
      }
    }
    if (st.dirty) scheduleDescStoreSave(cfg, st);
  }
  function getStoredDesc(cfg, st, vid) {
    if (!vid) return null;
    ensureDescStoreLoaded(cfg, st);
    const ttl = Math.max(1, Number(cfg.descStore.ttlMs) || 36e5);
    const tNow = nowMs();
    const obj = st.obj || {};
    const e = obj[vid];
    if (!e) return null;
    const t = Number(e.t || 0);
    if (!t || tNow - t >= ttl) {
      delete obj[vid];
      st.dirty = true;
      scheduleDescStoreSave(cfg, st);
      return null;
    }
    return e.d;
  }
  function setStoredDesc(cfg, st, vid, desc) {
    if (!vid) return;
    ensureDescStoreLoaded(cfg, st);
    const obj = st.obj || {};
    obj[vid] = { t: nowMs(), d: String(desc || "") };
    st.dirty = true;
    pruneDescStore(cfg, st);
    scheduleDescStoreSave(cfg, st);
  }
  function skNorm(cfg) {
    const s = cfg.list.desc.skeleton || {};
    const lines = Math.max(1, Math.min(3, Number(s.lines) || 1));
    const gap = Math.max(0, Number(s.lineGap) || 6);
    const heights = Array.isArray(s.lineHeights) ? s.lineHeights : [12, 12, 12];
    const widths = Array.isArray(s.lineWidthsPct) ? s.lineWidthsPct : [82, 74, 58];
    const h = (i) => Math.max(10, Number(heights[i] ?? heights[0] ?? 12));
    const w = (i) => Math.max(35, Math.min(100, Number(widths[i] ?? widths[0] ?? 82)));
    const r = Math.max(6, Number(s.radius) || 9);
    const maxW = Math.max(160, Number(s.maxW) || 520);
    const ms = Math.max(650, Number(s.animMs) || 5e3);
    return { enabled: s.enabled, lines, gap, h, w, r, maxW, ms };
  }
  const patchesCss = 'html[__ATTR__="list"] #content.ytd-rich-section-renderer{ margin:0 !important; }\r\n\r\nytd-masthead{ position:sticky !important; top:0 !important; z-index:3000 !important; }\r\n#title-text{ position:relative !important; z-index:1 !important; }\r\n\r\n#__TOGGLE_ID__{\r\n  display:inline-flex;align-items:center;gap:10px;margin-left:12px;\r\n  position:relative;\r\n}\r\n#__TOGGLE_ID__ .__BTN__{\r\n  width:36px;height:36px;border-radius:10px;\r\n  border:1px solid var(--yt-spec-10-percent-layer, rgba(255,255,255,.12));\r\n  background:rgba(255,255,255,.06);\r\n  color:var(--yt-spec-text-primary, #fff);\r\n  display:inline-flex;align-items:center;justify-content:center;\r\n  padding:0;cursor:pointer;\r\n  box-sizing:border-box;\r\n  user-select:none;\r\n  -webkit-user-select:none;\r\n  touch-action:manipulation;\r\n}\r\n#__TOGGLE_ID__ .__BTN__:hover{ background:rgba(255,255,255,.10); border-color:rgba(255,255,255,.18) }\r\n#__TOGGLE_ID__ .__BTN__[data-active]{ background:rgba(255,255,255,.16); border-color:rgba(255,255,255,.28) }\r\n#__TOGGLE_ID__ .__BTN__ .__BTN_ICON__{ width:20px;height:20px;display:block; pointer-events:none; }\r\n#__TOGGLE_ID__ .__BTN__ svg{ width:20px;height:20px;fill:currentColor;opacity:.95;display:block; pointer-events:none; }\r\n\r\nytd-browse[page-subtype="subscriptions"] ytd-shelf-renderer .grid-subheader #title-container,\r\nytd-two-column-browse-results-renderer[page-subtype="subscriptions"] ytd-shelf-renderer .grid-subheader #title-container{\r\n  display:flex !important;align-items:center !important;\r\n  position:relative !important;\r\n}\r\nytd-browse[page-subtype="subscriptions"] ytd-shelf-renderer .grid-subheader #title-container #spacer,\r\nytd-two-column-browse-results-renderer[page-subtype="subscriptions"] ytd-shelf-renderer .grid-subheader #title-container #spacer{\r\n  flex:1 1 auto !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] ytd-rich-grid-renderer{ --ytd-rich-grid-item-max-width: 100% !important; }\r\nhtml[__ATTR__="list"] ytd-rich-grid-renderer #contents{\r\n  display:block !important;\r\n  max-width: __MAX_W__ !important;\r\n  margin:0 auto !important;\r\n  width:100% !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer{\r\n  display:block !important;width:100% !important;\r\n  padding:__ROW_PAD_Y__ 0 !important;margin:0 !important;\r\n  border-bottom:1px solid var(--yt-spec-10-percent-layer, var(--yslv-sep, rgba(0,0,0,.10))) !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer.__ISSHORT__{\r\n  display:inline-block !important;\r\n  width:auto !important;\r\n  padding:0 10px 0 0 !important;\r\n  margin:0 !important;\r\n  border-bottom:0 !important;\r\n  vertical-align:top !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer:first-child{ padding-top:0 !important; }\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer:last-child{ border-bottom:0 !important; }\r\n\r\nhtml[__ATTR__="list"] .__ROWHEAD__{\r\n  display:flex !important;align-items:center !important;\r\n  gap:__HEAD_GAP__ !important;margin:0 0 __HEAD_MB__ 0 !important;\r\n}\r\nhtml[__ATTR__="list"] .__ROWHEAD__ .yt-lockup-metadata-view-model__avatar{\r\n  display:flex !important;\r\n  width:__HEAD_AV__ !important;height:__HEAD_AV__ !important;min-width:__HEAD_AV__ !important;\r\n  margin:0 !important;\r\n}\r\nhtml[__ATTR__="list"] .__ROWHEAD__ yt-avatar-shape,\r\nhtml[__ATTR__="list"] .__ROWHEAD__ .yt-spec-avatar-shape{\r\n  width:__HEAD_AV__ !important;height:__HEAD_AV__ !important;\r\n}\r\nhtml[__ATTR__="list"] .__ROWHEAD_NAME__{\r\n  color:var(--yt-spec-text-primary) !important;\r\n  text-decoration:none !important;\r\n  font-weight:700 !important;\r\n  font-size:18px !important;\r\n  line-height:1.15 !important;\r\n  display:inline-flex !important;\r\n  align-items:center !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] .yt-lockup-view-model.yt-lockup-view-model--vertical{\r\n  display:grid !important;\r\n  grid-template-columns:__THUMB_W__ minmax(0,1fr) auto !important;\r\n  grid-template-rows:auto auto !important;\r\n  column-gap:18px !important;row-gap:10px !important;\r\n  align-items:start !important;\r\n}\r\nhtml[__ATTR__="list"] .yt-lockup-view-model__content-image{\r\n  grid-column:1 !important;grid-row:1 / span 2 !important;\r\n  width:__THUMB_W__ !important;min-width:__THUMB_W__ !important;max-width:__THUMB_W__ !important;\r\n}\r\nhtml[__ATTR__="list"] .yt-lockup-view-model__content-image img{\r\n  width:100% !important;height:auto !important;border-radius:__RADIUS__ !important;\r\n  object-fit:cover !important;display:block !important;\r\n}\r\nhtml[__ATTR__="list"] .yt-lockup-view-model__metadata{\r\n  grid-column:2 !important;grid-row:1 / span 2 !important;min-width:0 !important;\r\n}\r\nhtml[__ATTR__="list"] .yt-lockup-metadata-view-model__menu-button{\r\n  grid-column:3 !important;grid-row:1 !important;justify-self:end !important;align-self:start !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] .yt-lockup-metadata-view-model__title{\r\n  display:-webkit-box !important;-webkit-box-orient:vertical !important;-webkit-line-clamp:__TITLE_CLAMP__ !important;\r\n  overflow:hidden !important;white-space:normal !important;line-height:1.35 !important;font-weight:600 !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] .yt-lockup-view-model__metadata .yt-lockup-metadata-view-model__avatar{ display:none !important; }\r\n\r\nhtml[__ATTR__="list"] .yt-lockup-metadata-view-model__metadata yt-content-metadata-view-model{\r\n  display:block !important;\r\n  position:absolute !important;\r\n  left:-99999px !important;\r\n  top:auto !important;\r\n  width:1px !important;\r\n  height:1px !important;\r\n  overflow:hidden !important;\r\n  opacity:0 !important;\r\n  pointer-events:none !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] .__METAROW__{\r\n  display:flex !important;align-items:center !important;\r\n  gap:__META_GAP__ !important;margin-top:6px !important;min-width:0 !important;\r\n}\r\nhtml[__ATTR__="list"] .__METACH__{\r\n  min-width:0 !important;\r\n  color:var(--yt-spec-text-secondary) !important;\r\n  font-size:12px !important;line-height:1.35 !important;\r\n  white-space:nowrap !important;overflow:hidden !important;text-overflow:ellipsis !important;\r\n  flex:0 1 auto !important;\r\n  display:flex !important;\r\n  align-items:center !important;\r\n}\r\nhtml[__ATTR__="list"] .__METACH__ a{\r\n  color:inherit !important;\r\n  text-decoration:none !important;\r\n  display:inline-flex !important;\r\n  align-items:center !important;\r\n  min-width:0 !important;\r\n}\r\nhtml[__ATTR__="list"] .__METACH__ a > span{\r\n  display:inline-flex !important;\r\n  align-items:center !important;\r\n}\r\nhtml[__ATTR__="list"] .__METACH__ .yt-icon-shape.ytSpecIconShapeHost{\r\n  margin-right:2px !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] .__METART__{\r\n  color:var(--yt-spec-text-secondary) !important;\r\n  font-size:12px !important;line-height:1.35 !important;\r\n  white-space:nowrap !important;\r\n  opacity:.95 !important;\r\n  flex:0 0 auto !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] .__DESC__{\r\n  margin-top:__DESC_MT__ !important;\r\n  font-size:12px !important;line-height:1.45 !important;\r\n  color:var(--yt-spec-text-secondary) !important;\r\n  display:-webkit-box !important;-webkit-box-orient:vertical !important;-webkit-line-clamp:__DESC_CLAMP__ !important;\r\n  overflow:hidden !important;white-space:normal !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] .__DESC__.__DESCSKEL__{\r\n  display:__SKL_ENABLED__ !important;\r\n  -webkit-line-clamp:unset !important;\r\n  overflow:hidden !important;\r\n  color:transparent !important;\r\n  position:relative !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] .__DESC__.__DESCSKEL__ > span{\r\n  display:block !important;\r\n  border-radius:__SKL_R__ !important;\r\n  opacity:1 !important;\r\n  pointer-events:none !important;\r\n\r\n  background-color:var(--yslv-skel-base, rgba(0,0,0,.10)) !important;\r\n  background-image:linear-gradient(\r\n    90deg,\r\n    var(--yslv-skel-base, rgba(0,0,0,.10)) 0%,\r\n    var(--yt-spec-10-percent-layer, rgba(255,255,255,.20)) 50%,\r\n    var(--yslv-skel-base, rgba(0,0,0,.10)) 100%\r\n  ) !important;\r\n  background-size:220% 100% !important;\r\n  background-position:var(__SHIMMER_VAR__, 200%) 0 !important;\r\n\r\n  transform:translateZ(0) !important;\r\n  will-change:background-position !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] .__DESC__.__DESCSKEL__ > span:nth-child(1){\r\n  height:__SKL_H1__ !important;\r\n  width:min(__SKL_MAXW__, __SKL_W1__) !important;\r\n  margin-top:0 !important;\r\n}\r\nhtml[__ATTR__="list"] .__DESC__.__DESCSKEL__ > span:nth-child(2){\r\n  height:__SKL_H2__ !important;\r\n  width:min(__SKL_MAXW__, __SKL_W2__) !important;\r\n  margin-top:__SKL_GAP__ !important;\r\n}\r\nhtml[__ATTR__="list"] .__DESC__.__DESCSKEL__ > span:nth-child(3){\r\n  height:__SKL_H3__ !important;\r\n  width:min(__SKL_MAXW__, __SKL_W3__) !important;\r\n  margin-top:__SKL_GAP__ !important;\r\n}\r\n\r\nhtml[__ATTR__="list"]{\r\n  __SHORTW_VAR__: __SHORT_W__;\r\n}\r\n\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer.__ISSHORT__ #content{\r\n  width:var(__SHORTW_VAR__) !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer.__ISSHORT__ a.reel-item-endpoint,\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer.__ISSHORT__ a.shortsLockupViewModelHostEndpoint.reel-item-endpoint{\r\n  width:var(__SHORTW_VAR__) !important;\r\n  display:block !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer.__ISSHORT__ yt-thumbnail-view-model,\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer.__ISSHORT__ .ytThumbnailViewModelImage,\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer.__ISSHORT__ .shortsLockupViewModelHostThumbnailParentContainer{\r\n  width:var(__SHORTW_VAR__) !important;\r\n  aspect-ratio:2/3 !important;\r\n}\r\n\r\nhtml[__ATTR__="list"] ytd-rich-item-renderer.__ISSHORT__ img.ytCoreImageHost{\r\n  width:100% !important;\r\n  height:100% !important;\r\n  object-fit:cover !important;\r\n  border-radius:__RADIUS__ !important;\r\n  display:block !important;\r\n}\r\n\r\nhtml[data-yslv-subs-view="list"] ytd-rich-shelf-renderer #dismissible{\r\n  margin-top:2rem !important;\r\n}\r\nhtml[data-yslv-subs-view="list"] ytd-rich-item-renderer.yslv-is-short > #content{\r\n  padding-bottom:3rem !important;\r\n}\r\n\r\nhtml[dark][__ATTR__="list"]{ --yslv-sep: rgba(255,255,255,.18); --yslv-skel-base: rgba(255,255,255,.10); }\r\nhtml:not([dark])[__ATTR__="list"]{ --yslv-sep: rgba(0,0,0,.10); --yslv-skel-base: rgba(0,0,0,.10); }\r\n\r\n/* "Notify Me" buttons for upcoming videos are full width fix by: https://greasyfork.org/en/users/1568138-boxfriend */\r\nhtml[__ATTR__="list"] .ytLockupAttachmentsViewModelHost{\r\n  width: fit-content !important;\r\n}\r\n\r\n/* Patch: rich-section spacing */\r\nhtml[__ATTR__="list"]\r\nytd-counterfactual-renderer.ytd-rich-section-renderer,\r\nhtml[__ATTR__="list"]\r\n#content.ytd-rich-section-renderer > ytd-rich-list-header-renderer.ytd-rich-section-renderer,\r\nhtml[__ATTR__="list"]\r\n#content.ytd-rich-section-renderer > ytd-shelf-renderer.ytd-rich-section-renderer,\r\nhtml[__ATTR__="list"]\r\n#content.ytd-rich-section-renderer > ytd-rich-shelf-renderer.ytd-rich-section-renderer,\r\nhtml[__ATTR__="list"]\r\n#content.ytd-rich-section-renderer > ytd-inline-survey-renderer.ytd-rich-section-renderer[is-dismissed]{\r\n  margin-bottom:1rem !important;\r\n}\r\n\r\n/* Patch: mini guide visible margin */\r\nhtml[__ATTR__="list"]\r\nytd-app[mini-guide-visible] ytd-page-manager.ytd-app{\r\n  margin-left: unset !important;\r\n}\r\n';
  function px(n) {
    return `${n}px`;
  }
  function cssMaxWidth(v) {
    if (typeof v === "number") return `${Math.max(0, v)}px`;
    const s = String(v).trim();
    return s || "1120px";
  }
  function applyTokens(template, tokens) {
    let out = template;
    for (const [k, v] of Object.entries(tokens)) out = out.split(k).join(v);
    return out;
  }
  function skeletonTokens(S) {
    const enabled = S.enabled ? "block" : "none";
    return {
      "__SKL_ENABLED__": enabled,
      "__SKL_LINES__": String(S.lines),
      "__SKL_GAP__": px(S.gap),
      "__SKL_R__": px(S.r),
      "__SKL_MAXW__": px(S.maxW),
      "__SKL_MS__": String(S.ms),
      "__SKL_H1__": px(S.h(0)),
      "__SKL_H2__": px(S.h(1)),
      "__SKL_H3__": px(S.h(2)),
      "__SKL_W1__": `${S.w(0)}%`,
      "__SKL_W2__": `${S.w(1)}%`,
      "__SKL_W3__": `${S.w(2)}%`
    };
  }
  function buildStyleText(cfg) {
    const L = cfg.list;
    const S = skNorm(cfg);
    const tokens = {
      "__ATTR__": cfg.attr.view,
      "__TOGGLE_ID__": cfg.ids.toggle,
      "__BTN__": cfg.cls.btn,
      "__BTN_ICON__": cfg.cls.btnIcon,
      "__ROWHEAD__": cfg.cls.rowHead,
      "__ROWHEAD_NAME__": cfg.cls.rowHeadName,
      "__METAROW__": cfg.cls.metaRow,
      "__METACH__": cfg.cls.metaCh,
      "__METART__": cfg.cls.metaRt,
      "__DESC__": cfg.cls.desc,
      "__DESCSKEL__": cfg.cls.descSkel,
      "__ISSHORT__": cfg.cls.isShort,
      "__MAX_W__": cssMaxWidth(L.maxWidth),
      "__ROW_PAD_Y__": px(Math.max(8, Number(L.rowPadY) || 22)),
      "__THUMB_W__": px(Math.max(240, Number(L.thumbW) || 240)),
      "__RADIUS__": px(Math.max(0, Number(L.thumbRadius) || 14)),
      "__TITLE_CLAMP__": String(Math.max(1, Number(L.titleClamp) || 2)),
      "__DESC_CLAMP__": String(Math.max(1, Number(L.descClamp) || 2)),
      "__DESC_MT__": px(Math.max(4, Number(L.desc.marginTop) || 10)),
      "__HEAD_GAP__": px(Math.max(6, Number(L.rowHead.gap) || 12)),
      "__HEAD_MB__": px(Math.max(6, Number(L.rowHead.marginBottom) || 20)),
      "__HEAD_AV__": px(Math.max(20, Number(L.rowHead.avatarSize) || 32)),
      "__META_GAP__": px(Math.max(6, Number(L.metaRow.gap) || 8)),
      "__SHORT_W__": px(Math.max(110, Number(L.shorts.cardW) || 170)),
      "__SHIMMER_VAR__": cfg.cssVars.shimmerX,
      "__SHORTW_VAR__": cfg.cssVars.shortW,
      ...skeletonTokens(S)
    };
    return applyTokens(String(patchesCss), tokens).trim();
  }
  function ensureStyle(cfg, state) {
    if (state.styleEl) return;
    const el = document.createElement("style");
    el.id = cfg.ids.style;
    el.textContent = buildStyleText(cfg);
    document.documentElement.appendChild(el);
    state.styleEl = el;
  }
  function clearChildren(el) {
    if (!el) return;
    while (el.firstChild) el.removeChild(el.firstChild);
  }
  function normalizeText(s) {
    return String(s || "").replace(/\u200B/g, "").replace(/\s+/g, " ").trim();
  }
  function cloneInto(dest, src) {
    if (!dest) return;
    clearChildren(dest);
    if (!src) return;
    const frag = document.createDocumentFragment();
    for (const n of Array.from(src.childNodes || [])) frag.appendChild(n.cloneNode(true));
    const hosts = Array.from(frag.querySelectorAll?.(".ytIconWrapperHost, .yt-icon-shape") || []);
    for (const host of hosts) {
      const el = host;
      if (!el.querySelector("svg")) el.remove();
    }
    dest.appendChild(frag);
  }
  function setTextOnly(dest, txt) {
    if (!dest) return;
    clearChildren(dest);
    dest.textContent = normalizeText(txt);
  }
  function svgEl(paths, viewBox) {
    const NS = "http://www.w3.org/2000/svg";
    const svg = document.createElementNS(NS, "svg");
    svg.setAttribute("viewBox", "0 0 24 24");
    svg.setAttribute("aria-hidden", "true");
    for (const d of paths) {
      const p = document.createElementNS(NS, "path");
      p.setAttribute("d", d);
      svg.appendChild(p);
    }
    return svg;
  }
  function paintToggle(cfg, state) {
    const root = document.getElementById(cfg.ids.toggle);
    if (!root) return;
    root.querySelectorAll("button[data-mode]").forEach((b) => {
      const m = b.getAttribute("data-mode");
      if (m === state.view) b.setAttribute("data-active", "");
      else b.removeAttribute("data-active");
    });
  }
  function removeToggle(cfg) {
    const root = document.getElementById(cfg.ids.toggle);
    if (root) root.remove();
  }
  function ensureToggle(cfg, state, deps) {
    const existing = document.getElementById(cfg.ids.toggle);
    if (existing && existing.isConnected) {
      paintToggle(cfg, state);
      return;
    }
    const subscribeBtn = getActiveSubsDoc().querySelector(cfg.toggleMountSelector);
    const titleContainer = subscribeBtn?.closest?.("#title-container") || null;
    if (!subscribeBtn || !titleContainer) return;
    document.querySelectorAll(`#${cfg.ids.toggle}`).forEach((n) => n.remove());
    const root = document.createElement("div");
    root.id = cfg.ids.toggle;
    const mkBtn = (mode, label, svg) => {
      const b = document.createElement("button");
      b.className = cfg.cls.btn;
      b.type = "button";
      b.setAttribute("data-mode", mode);
      b.setAttribute("aria-label", label);
      const ic = document.createElement("span");
      ic.className = cfg.cls.btnIcon;
      ic.appendChild(svg);
      b.appendChild(ic);
      return b;
    };
    const bGrid = mkBtn("grid", "Grid", svgEl(["M4 4h7v7H4V4zm9 0h7v7h-7V4zM4 13h7v7H4v-7zm9 0h7v7h-7v-7z"]));
    const bList = mkBtn(
      "list",
      "List",
      svgEl(["M4 6h3v3H4V6zm5 0h11v3H9V6zM4 11h3v3H4v-3zm5 0h11v3H9v-3zM4 16h3v3H4v-3zm5 0h11v3H9v-3z"])
    );
    root.appendChild(bGrid);
    root.appendChild(bList);
    root.addEventListener("click", (e) => {
      const target = e.target;
      const btn = target?.closest?.("button[data-mode]");
      if (!btn) return;
      const mode = btn.getAttribute("data-mode");
      if (mode !== "grid" && mode !== "list") return;
      const next = mode;
      if (next === state.view) return;
      deps.onSwitchView(next);
    });
    subscribeBtn.insertAdjacentElement("afterend", root);
    paintToggle(cfg, state);
  }
  function isShortsHref(href) {
    const h = String(href || "");
    return h.startsWith("/shorts/") || h.includes("youtube.com/shorts/");
  }
  function extractVideoIdFromHref(href) {
    const h = String(href || "");
    if (!h) return "";
    if (isShortsHref(h)) {
      try {
        const u = new URL(h, location.origin);
        const parts = u.pathname.split("/").filter(Boolean);
        const idx = parts.indexOf("shorts");
        const id = idx >= 0 ? String(parts[idx + 1] || "") : "";
        return id;
      } catch {
        const m = h.match(/\/shorts\/([^?&#/]+)/);
        return m ? m[1] ?? "" : "";
      }
    }
    try {
      const u = new URL(h, location.origin);
      return u.searchParams.get("v") || "";
    } catch {
      const m = h.match(/[?&]v=([^&]+)/);
      return m ? m[1] ?? "" : "";
    }
  }
  function pickPrimaryVideoAnchor(lockup) {
    return lockup.querySelector('a.yt-lockup-view-model__content-image[href^="/watch"]') || lockup.querySelector('a.yt-lockup-view-model__content-image[href^="/shorts/"]') || lockup.querySelector('a[href^="/watch"][id="thumbnail"]') || lockup.querySelector('a[href^="/shorts/"][id="thumbnail"]') || lockup.querySelector('a[href^="/shorts/"].reel-item-endpoint') || lockup.querySelector('a[href^="/watch"]') || lockup.querySelector('a[href^="/shorts/"]') || null;
  }
  function ensureDesc(cfg, state, store, textContainer, lockup) {
    let desc = textContainer.querySelector(`.${cfg.cls.desc}`);
    if (!desc) {
      desc = document.createElement("div");
      desc.className = cfg.cls.desc;
      textContainer.appendChild(desc);
    }
    const vLink = pickPrimaryVideoAnchor(lockup);
    const href = vLink?.getAttribute?.("href") || "";
    const vid = extractVideoIdFromHref(href);
    if (!vid) {
      desc.textContent = "";
      desc.style.display = "none";
      desc.classList.remove(cfg.cls.descSkel);
      delete desc.dataset.yslvVid;
      return;
    }
    desc.dataset.yslvVid = vid;
    const mem = state.descCache.get(vid);
    if (mem != null) {
      desc.textContent = mem;
      desc.style.display = mem ? "" : "none";
      desc.classList.remove(cfg.cls.descSkel);
      return;
    }
    const stored = getStoredDesc(cfg, store, vid);
    if (stored != null) {
      state.descCache.set(vid, stored);
      desc.textContent = stored;
      desc.style.display = stored ? "" : "none";
      desc.classList.remove(cfg.cls.descSkel);
      return;
    }
    const S = skNorm(cfg);
    if (!S.enabled) {
      desc.textContent = "";
      desc.style.display = "none";
      desc.classList.remove(cfg.cls.descSkel);
      return;
    }
    desc.style.display = "";
    desc.classList.add(cfg.cls.descSkel);
    const needs = desc.childElementCount !== S.lines || !desc.querySelector(":scope > span");
    if (needs) {
      clearChildren(desc);
      for (let i = 0; i < S.lines; i++) desc.appendChild(document.createElement("span"));
    }
  }
  function summarizeDesc(raw, sentenceCount, maxChars) {
    let s = String(raw || "").trim();
    if (!s) return "";
    s = s.replace(/\r/g, "").replace(/\n{2,}/g, "\n").replace(/[ \t]{2,}/g, " ").trim();
    const seg = typeof Intl !== "undefined" && Intl.Segmenter ? new Intl.Segmenter(void 0, { granularity: "sentence" }) : null;
    if (seg) {
      const out = [];
      for (const part of seg.segment(s)) {
        const t = String(part.segment || "").trim();
        if (!t) continue;
        out.push(t);
        if (out.length >= sentenceCount) break;
      }
      s = out.join(" ").trim();
    } else {
      const urls = [];
      s = s.replace(/\bhttps?:\/\/[^\s]+|\bwww\.[^\s]+/gi, (m) => {
        const k = `__YSU${urls.length}__`;
        urls.push(m);
        return k;
      });
      const parts = s.split(/(?<=[.!?])\s+/).map((x) => x.trim()).filter(Boolean);
      s = parts.slice(0, sentenceCount).join(" ").trim();
      s = s.replace(/__YSU(\d+)__/g, (_, i) => urls[Number(i)] || "");
    }
    if (s.length > maxChars) s = s.slice(0, maxChars).trimEnd() + "…";
    return s;
  }
  async function fetchDescriptionForVideoId(cfg, state, store, vid) {
    const F = cfg.list.descFetch;
    if (!F.enabled) return "";
    if (!vid) return "";
    const mem = state.descCache.get(vid);
    if (mem != null) return mem;
    const stored = getStoredDesc(cfg, store, vid);
    if (stored != null) {
      state.descCache.set(vid, stored);
      return stored;
    }
    if (state.descInFlight.has(vid)) return state.descInFlight.get(vid);
    if (state.descFetches >= F.maxTotalFetchesPerNav) return "";
    const p = (async () => {
      while (state.descActive >= F.maxConcurrent) {
        await new Promise((r) => setTimeout(r, 35));
      }
      state.descActive++;
      state.descFetches++;
      try {
        const res = await fetch(`https://www.youtube.com/watch?v=${encodeURIComponent(vid)}`, { credentials: "same-origin" });
        const html = await res.text();
        const m = html.match(/ytInitialPlayerResponse\s*=\s*(\{.*?});/s);
        if (!m) return "";
        const json = JSON.parse(m[1]);
        const raw = String(json?.videoDetails?.shortDescription || "").trim();
        if (!raw) return "";
        return summarizeDesc(raw, F.sentenceCount, F.maxChars);
      } catch {
        return "";
      } finally {
        state.descActive--;
      }
    })();
    state.descInFlight.set(vid, p);
    const out = await p;
    state.descInFlight.delete(vid);
    state.descCache.set(vid, out);
    setStoredDesc(cfg, store, vid, out);
    pruneDescStore(cfg, store);
    return out;
  }
  function updateDescDomForVid(cfg, vid, text) {
    const nodes = document.querySelectorAll(`.${cfg.cls.desc}[data-yslv-vid="${CSS.escape(vid)}"]`);
    for (const n of Array.from(nodes)) {
      const el = n;
      if (!el.isConnected) continue;
      el.classList.remove(cfg.cls.descSkel);
      clearChildren(el);
      el.textContent = text || "";
      el.style.display = text ? "" : "none";
    }
  }
  function buildDescQueueFromDom(cfg, state, store) {
    if (!state.active || state.view !== "list") return;
    const descs = document.querySelectorAll(`.${cfg.cls.desc}[data-yslv-vid]`);
    if (!descs.length) return;
    let sig = "";
    for (const d of Array.from(descs)) {
      const vid = d.dataset.yslvVid || "";
      if (!vid) continue;
      sig += `${vid}|`;
    }
    if (sig === state.lastQueueSig) return;
    state.lastQueueSig = sig;
    for (const d of Array.from(descs)) {
      const vid = d.dataset.yslvVid || "";
      if (!vid) continue;
      const stored = getStoredDesc(cfg, store, vid);
      if (stored != null) {
        state.descCache.set(vid, stored);
        updateDescDomForVid(cfg, vid, stored);
        continue;
      }
      if (state.descCache.has(vid)) continue;
      if (state.descInFlight.has(vid)) continue;
      if (state.descQueued.has(vid)) continue;
      state.descQueued.add(vid);
      state.descQueue.push(vid);
    }
  }
  function pickChannelDisplaySource(lockup) {
    const a = lockup.querySelector('yt-content-metadata-view-model .yt-content-metadata-view-model__metadata-row a[href^="/@"]') || lockup.querySelector('yt-content-metadata-view-model .yt-content-metadata-view-model__metadata-row a[href^="/channel/"]') || lockup.querySelector('a[href^="/@"]') || lockup.querySelector('a[href^="/channel/"]') || null;
    if (a) return a;
    return lockup.querySelector(
      "yt-content-metadata-view-model .yt-content-metadata-view-model__metadata-row span.yt-content-metadata-view-model__metadata-text"
    ) || null;
  }
  function pickChannelAnchor(lockup) {
    return lockup.querySelector('yt-content-metadata-view-model .yt-content-metadata-view-model__metadata-row a[href^="/@"]') || lockup.querySelector('yt-content-metadata-view-model .yt-content-metadata-view-model__metadata-row a[href^="/channel/"]') || lockup.querySelector('a[href^="/@"]') || lockup.querySelector('a[href^="/channel/"]') || null;
  }
  function getChannelHref(lockup) {
    const a = pickChannelAnchor(lockup);
    const href = String(a?.getAttribute?.("href") || "").trim();
    if (!href) return "";
    try {
      return new URL(href, location.origin).href;
    } catch {
      return "";
    }
  }
  function getChannelName(lockup) {
    const src = pickChannelDisplaySource(lockup);
    return normalizeText(src?.textContent || "");
  }
  function isIconish(node) {
    if (!node) return false;
    if (node.matches("yt-icon-shape, .yt-icon-shape")) return true;
    if (node.querySelector("yt-icon-shape, .yt-icon-shape")) return true;
    if (node.querySelector("svg, img")) return true;
    if (node.getAttribute("role") === "img") return true;
    if (node.querySelector('[role="img"]')) return true;
    return false;
  }
  function collectBadgeNodesFromAnchor(a) {
    const out = [];
    if (!a) return out;
    const candidates = a.querySelectorAll(
      ".yt-core-attributed-string__image-element, .ytIconWrapperHost, .yt-core-attributed-string__image-element--image-alignment-vertical-center, yt-icon-shape, .yt-icon-shape"
    );
    const seen = /* @__PURE__ */ new Set();
    for (const el of Array.from(candidates)) {
      const node = el;
      let root = node.closest(".yt-core-attributed-string__image-element") || node.closest(".ytIconWrapperHost") || node.closest(".yt-core-attributed-string__image-element--image-alignment-vertical-center") || node;
      if (!root || root === a) continue;
      if (!isIconish(root)) continue;
      const key = `${root.tagName}|${root.getAttribute("class") || ""}|${root.getAttribute("aria-label") || ""}`;
      if (seen.has(key)) continue;
      seen.add(key);
      out.push(root);
    }
    return out;
  }
  function normalizeMetaAnchorInPlace(a, nameText) {
    if (!a) return;
    const name = normalizeText(nameText);
    if (!name) return;
    const badgeRoots = collectBadgeNodesFromAnchor(a);
    const badges = [];
    for (const r of badgeRoots) {
      if (!r.isConnected) continue;
      badges.push(r);
    }
    for (const b of badges) {
      try {
        b.parentNode?.removeChild(b);
      } catch {
      }
    }
    clearChildren(a);
    a.appendChild(document.createTextNode(name));
    for (const b of badges) {
      if (!isIconish(b)) continue;
      const wrap = document.createElement("span");
      wrap.style.display = "inline-flex";
      wrap.style.alignItems = "center";
      wrap.style.marginLeft = "4px";
      wrap.appendChild(b);
      a.appendChild(wrap);
    }
    for (const s of Array.from(a.querySelectorAll(":scope > span"))) {
      const el = s;
      if (!el.querySelector || !isIconish(el)) el.remove();
    }
  }
  function detachMetaAnchorOnce(cfg, state, lockup) {
    if (state.movedMetaAnchors.has(lockup)) return state.movedMetaAnchors.get(lockup)?.a || null;
    const a = pickChannelAnchor(lockup);
    if (!a || !a.parentNode) return null;
    const parent = a.parentNode;
    const nextSibling = a.nextSibling;
    state.movedMetaAnchors.set(lockup, { a, parent, nextSibling });
    return a;
  }
  function restoreMovedMetaAnchors(state) {
    const entries = [];
    document.querySelectorAll("yt-lockup-view-model").forEach((lockup) => {
      const info = state.movedMetaAnchors.get(lockup);
      if (!info) return;
      entries.push(info);
    });
    for (const info of entries) {
      const { a, parent, nextSibling } = info;
      if (!a || !parent) continue;
      if (!a.isConnected) continue;
      if (a.parentNode === parent) continue;
      try {
        if (nextSibling && nextSibling.parentNode === parent) parent.insertBefore(a, nextSibling);
        else parent.appendChild(a);
      } catch {
      }
    }
    state.movedMetaAnchors = /* @__PURE__ */ new WeakMap();
  }
  function setHeaderNameTextOnly(destLink, lockup) {
    if (!destLink) return;
    const href = getChannelHref(lockup);
    destLink.href = href || "javascript:void(0)";
    const src = pickChannelDisplaySource(lockup);
    setTextOnly(destLink, src?.textContent || "");
  }
  function getRightMetaRowsText(lockup) {
    const chName = getChannelName(lockup);
    const rows = Array.from(lockup.querySelectorAll("yt-content-metadata-view-model .yt-content-metadata-view-model__metadata-row")).map((r) => normalizeText(r.textContent || "")).filter(Boolean).filter((t) => chName ? t !== chName : true);
    if (!rows.length) return "";
    const out = [];
    const seen = /* @__PURE__ */ new Set();
    for (const t of rows) {
      const k = t.toLowerCase();
      if (seen.has(k)) continue;
      seen.add(k);
      out.push(t);
    }
    if (!out.length) return "";
    if (out.length === 1) {
      if (chName && out[0] === chName) return "";
      return out[0] ?? "";
    }
    return out.slice(1).join(" • ");
  }
  function ensureInlineMeta(cfg, state, textContainer, lockup) {
    let row = textContainer.querySelector(`.${cfg.cls.metaRow}`);
    if (!row) {
      row = document.createElement("div");
      row.className = cfg.cls.metaRow;
      const heading = textContainer.querySelector(".yt-lockup-metadata-view-model__heading-reset") || textContainer.querySelector("h3");
      if (heading && heading.parentNode) heading.parentNode.insertBefore(row, heading.nextSibling);
      else textContainer.appendChild(row);
    }
    row.style.display = "flex";
    let left = row.querySelector(`:scope > .${cfg.cls.metaCh}`);
    if (!left) {
      left = document.createElement("div");
      left.className = cfg.cls.metaCh;
      row.appendChild(left);
    }
    const srcA = detachMetaAnchorOnce(cfg, state, lockup);
    const chName = getChannelName(lockup);
    if (srcA) {
      try {
        srcA.style.margin = "0";
      } catch {
      }
      normalizeMetaAnchorInPlace(srcA, chName);
      clearChildren(left);
      left.appendChild(srcA);
    } else {
      let link = left.querySelector("a");
      if (!link) {
        link = document.createElement("a");
        left.appendChild(link);
      }
      link.href = getChannelHref(lockup) || "javascript:void(0)";
      const src = pickChannelDisplaySource(lockup);
      if (src) cloneInto(link, src);
      else setTextOnly(link, chName || "");
    }
    const right = getRightMetaRowsText(lockup);
    let r = row.querySelector(`:scope > .${cfg.cls.metaRt}`);
    if (right) {
      if (!r) {
        r = document.createElement("div");
        r.className = cfg.cls.metaRt;
        row.appendChild(r);
      }
      r.textContent = right;
      r.style.display = "";
    } else if (r) {
      r.textContent = "";
      r.style.display = "none";
    }
    return row;
  }
  function ensureRowHeader(cfg, state, item, lockup) {
    if (!cfg.list.rowHead.enabled) return;
    let head = item.querySelector(`:scope > .${cfg.cls.rowHead}`);
    if (!head) {
      head = document.createElement("div");
      head.className = cfg.cls.rowHead;
      item.prepend(head);
    }
    head.style.display = "flex";
    let name = head.querySelector(`:scope > a.${cfg.cls.rowHeadName}`);
    if (!name) {
      name = document.createElement("a");
      name.className = cfg.cls.rowHeadName;
      head.appendChild(name);
    }
    setHeaderNameTextOnly(name, lockup);
    if (state.movedAvatars.has(item)) return;
    const avatarEl = lockup.querySelector(".yt-lockup-metadata-view-model__avatar");
    if (!avatarEl || !avatarEl.parentNode) return;
    const parent = avatarEl.parentNode;
    const nextSibling = avatarEl.nextSibling;
    state.movedAvatars.set(item, { avatarEl, parent, nextSibling });
    try {
      head.insertBefore(avatarEl, head.firstChild);
    } catch {
    }
  }
  function restoreMovedAvatars(state) {
    document.querySelectorAll("ytd-rich-item-renderer").forEach((item) => {
      const info = state.movedAvatars.get(item);
      if (!info) return;
      const { avatarEl, parent, nextSibling } = info;
      if (!avatarEl || !parent) return;
      if (!avatarEl.isConnected) return;
      if (avatarEl.parentNode === parent) return;
      try {
        if (nextSibling && nextSibling.parentNode === parent) parent.insertBefore(avatarEl, nextSibling);
        else parent.appendChild(avatarEl);
      } catch {
      }
    });
    state.movedAvatars = /* @__PURE__ */ new WeakMap();
  }
  function patchItem(cfg, state, store, item) {
    if (!state.active || state.view !== "list") return;
    if (item.tagName !== "YTD-RICH-ITEM-RENDERER") return;
    if (state.processedItems.has(item)) return;
    const shortsLockup = item.querySelector("ytm-shorts-lockup-view-model-v2, ytm-shorts-lockup-view-model");
    if (shortsLockup && cfg.list.shorts.enabled) {
      state.processedItems.add(item);
      item.classList.add(cfg.cls.isShort);
      return;
    }
    item.classList.remove(cfg.cls.isShort);
    const lockup = item.querySelector("yt-lockup-view-model");
    if (!lockup) return;
    const textContainer = lockup.querySelector(".yt-lockup-metadata-view-model__text-container") || lockup.querySelector("yt-lockup-metadata-view-model");
    if (!textContainer) return;
    state.processedItems.add(item);
    ensureRowHeader(cfg, state, item, lockup);
    ensureInlineMeta(cfg, state, textContainer, lockup);
    ensureDesc(cfg, state, store, textContainer, lockup);
  }
  function cleanupListArtifacts(cfg, state) {
    restoreMovedAvatars(state);
    restoreMovedMetaAnchors(state);
    document.querySelectorAll(`.${cfg.cls.rowHead}`).forEach((n) => n.remove());
    document.querySelectorAll(`.${cfg.cls.metaRow}`).forEach((n) => n.remove());
    document.querySelectorAll(`.${cfg.cls.desc}`).forEach((n) => n.remove());
    state.descQueue.length = 0;
    state.descQueued.clear();
    state.lastQueueSig = "";
  }
  function enqueue(cfg, state, store, node) {
    if (!state.active || state.view !== "list") return;
    if (node.tagName === "YTD-RICH-ITEM-RENDERER") {
      if (state.qSet.has(node)) return;
      state.qSet.add(node);
      state.q.push(node);
      scheduleProcess(cfg, state, store);
      return;
    }
    const found = node.querySelectorAll ? node.querySelectorAll("ytd-rich-item-renderer") : [];
    for (const it of Array.from(found)) enqueue(cfg, state, store, it);
  }
  function scheduleProcess(cfg, state, store) {
    if (state.processing) return;
    state.processing = true;
    const run = () => {
      state.processing = false;
      processQueue(cfg, state, store);
    };
    const ric = window.requestIdleCallback;
    if (ric) ric(run, { timeout: 300 });
    else setTimeout(run, 80);
  }
  function processQueue(cfg, state, store) {
    if (!state.active || state.view !== "list") {
      state.q.length = 0;
      state.qSet.clear();
      return;
    }
    let n = 0;
    while (state.q.length && n < cfg.perf.maxItemsPerTick) {
      const item = state.q.shift();
      if (!item) continue;
      state.qSet.delete(item);
      patchItem(cfg, state, store, item);
      n++;
    }
    if (state.q.length) scheduleProcess(cfg, state, store);
  }
  function enqueueAllOnce(cfg, state, store) {
    if (!state.active || state.view !== "list") return;
    const root = getActiveSubsRoot();
    const scope = root && root.querySelectorAll ? root : document;
    const items = scope.querySelectorAll?.("ytd-rich-item-renderer") || [];
    for (const it of Array.from(items)) enqueue(cfg, state, store, it);
  }
  function attachObserver(cfg, state, store) {
    if (!state.active) return;
    const target = getActiveSubsRoot() || document.documentElement;
    if (state.observedTarget === target && state.mo) return;
    state.mo?.disconnect();
    state.observedTarget = target;
    state.mo = new MutationObserver((muts) => {
      if (!state.active || state.view !== "list") return;
      for (const m of muts) {
        for (const node of Array.from(m.addedNodes)) {
          if (node && node.nodeType === 1) enqueue(cfg, state, store, node);
        }
      }
    });
    state.mo.observe(target, { childList: true, subtree: true });
  }
  function attachPageManagerObserver(cfg, state, store, onTick) {
    if (state.pmMo) return;
    const pm = document.querySelector("ytd-page-manager");
    if (!pm) return;
    state.pmMo = new MutationObserver(() => {
      if (!state.active) return;
      onTick();
      if (state.view === "list") {
        setTimeout(() => {
          if (!state.active || state.view !== "list") return;
          enqueueAllOnce(cfg, state, store);
        }, 60);
      }
    });
    state.pmMo.observe(pm, { childList: true, subtree: true });
  }
  function hasSkeletons(cfg) {
    return !!document.querySelector(`.${cfg.cls.desc}.${cfg.cls.descSkel}`);
  }
  function stopShimmer(cfg, shimmer) {
    shimmer.running = false;
    if (shimmer.raf) cancelAnimationFrame(shimmer.raf);
    shimmer.raf = 0;
    document.documentElement.style.removeProperty(cfg.cssVars.shimmerX);
  }
  function startShimmer(cfg, state, shimmer) {
    if (shimmer.running) return;
    shimmer.running = true;
    shimmer.t0 = performance.now();
    const tick = (t) => {
      if (!shimmer.running) return;
      const S = skNorm(cfg);
      if (!state.active || state.view !== "list" || !S.enabled || !hasSkeletons(cfg)) {
        stopShimmer(cfg, shimmer);
        return;
      }
      const phase = (t - shimmer.t0) % S.ms / S.ms;
      const x = 200 - phase * 400;
      document.documentElement.style.setProperty(cfg.cssVars.shimmerX, `${x}%`);
      shimmer.raf = requestAnimationFrame(tick);
    };
    shimmer.raf = requestAnimationFrame(tick);
  }
  async function pumpDescQueue(cfg, state, store) {
    if (state.descPumpRunning) return;
    state.descPumpRunning = true;
    try {
      while (state.active && state.view === "list" && state.descQueue.length) {
        const vid = state.descQueue.shift();
        if (!vid) continue;
        state.descQueued.delete(vid);
        const txt = await fetchDescriptionForVideoId(cfg, state, store, vid);
        updateDescDomForVid(cfg, vid, txt || "");
      }
    } finally {
      state.descPumpRunning = false;
    }
  }
  function ensureDescQueueLoop(cfg, state, store, shimmer) {
    if (state.descTimer) clearInterval(state.descTimer);
    if (!state.active) return;
    state.descTimer = window.setInterval(() => {
      if (!state.active || state.view !== "list") {
        stopShimmer(cfg, shimmer);
        return;
      }
      pruneDescStore(cfg, store);
      buildDescQueueFromDom(cfg, state, store);
      void pumpDescQueue(cfg, state, store);
      if (document.querySelector(`.${cfg.cls.desc}.${cfg.cls.descSkel}`)) startShimmer(cfg, state, shimmer);
      else stopShimmer(cfg, shimmer);
    }, cfg.perf.descQueueIntervalMs);
  }
  function createApp(cfg, state, shimmer, store) {
    function resetNavState() {
      state.processedItems = /* @__PURE__ */ new WeakSet();
      state.q.length = 0;
      state.qSet.clear();
      state.descInFlight.clear();
      state.descCache.clear();
      state.descFetches = 0;
      state.descActive = 0;
      state.descQueue.length = 0;
      state.descQueued.clear();
      state.descPumpRunning = false;
      state.lastQueueSig = "";
      state.observedTarget = null;
    }
    function teardown() {
      stopShimmer(cfg, shimmer);
      if (state.view === "list") {
        cleanupListArtifacts(cfg, state);
      }
      state.mo?.disconnect();
      state.mo = null;
      state.observedTarget = null;
      if (state.descTimer) {
        clearInterval(state.descTimer);
        state.descTimer = 0;
      }
      resetNavState();
      removeToggle(cfg);
      clearViewAttr(cfg);
    }
    function ensureToggleMountLoop() {
      if (!state.active) return;
      ensureToggle(cfg, state, {
        onSwitchView(next) {
          if (next === state.view) return;
          if (state.view === "list") {
            cleanupListArtifacts(cfg, state);
            restoreMovedAvatars(state);
            restoreMovedMetaAnchors(state);
          }
          resetNavState();
          state.view = next;
          saveView(cfg, next);
          applyViewAttr(cfg, next);
          attachObserver(cfg, state, store);
          ensureDescQueueLoop(cfg, state, store, shimmer);
          if (next === "list") {
            enqueueAllOnce(cfg, state, store);
            startShimmer(cfg, state, shimmer);
          } else {
            stopShimmer(cfg, shimmer);
          }
          paintToggle(cfg, state);
        }
      });
      if (state.active && !document.getElementById(cfg.ids.toggle)) setTimeout(ensureToggleMountLoop, 250);
    }
    function apply() {
      ensureDescStoreLoaded(cfg, store);
      pruneDescStore(cfg, store);
      ensureStyle(cfg, state);
      ensureToggleMountLoop();
      attachObserver(cfg, state, store);
      attachPageManagerObserver(cfg, state, store, () => {
        attachObserver(cfg, state, store);
        ensureToggleMountLoop();
      });
      ensureDescQueueLoop(cfg, state, store, shimmer);
      if (state.view === "list") {
        enqueueAllOnce(cfg, state, store);
        startShimmer(cfg, state, shimmer);
      } else {
        stopShimmer(cfg, shimmer);
      }
    }
    function syncActive(isNavFinish) {
      const shouldBeActive = isSubsPage();
      const sig = pageSig();
      if (shouldBeActive && !state.active) {
        state.active = true;
        state.lastPageSig = sig;
        state.view = loadView(cfg);
        applyViewAttr(cfg, state.view);
        apply();
        return;
      }
      if (!shouldBeActive && state.active) {
        state.active = false;
        state.lastPageSig = sig;
        teardown();
        return;
      }
      if (shouldBeActive && state.active) {
        ensureToggleMountLoop();
        paintToggle(cfg, state);
        attachObserver(cfg, state, store);
        if (state.view === "list") {
          if (sig !== state.lastPageSig) {
            state.lastPageSig = sig;
            resetNavState();
            enqueueAllOnce(cfg, state, store);
            startShimmer(cfg, state, shimmer);
          }
        } else {
          stopShimmer(cfg, shimmer);
        }
      }
    }
    function init() {
      syncActive();
      window.addEventListener("yt-navigate-finish", () => syncActive(), { passive: true });
      window.addEventListener("popstate", () => syncActive(), { passive: true });
      setTimeout(() => {
        attachPageManagerObserver(cfg, state, store, () => {
          attachObserver(cfg, state, store);
          ensureToggleMountLoop();
        });
      }, 250);
    }
    return { init };
  }
  function initYSLV() {
    const state = createState();
    const shimmer = createShimmerState();
    const store = createDescStoreState();
    const app = createApp(CFG, state, shimmer, store);
    app.init();
  }
  (() => {
    initYSLV();
  })();
})();