YouTube Logo Hover Menu

YouTubeロゴにホバーすると、履歴、後で見る、再生リスト、高評価動画に素早くアクセスできるメニューを表示します。

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         YouTube Logo Hover Menu
// @name:en      YouTube Logo Hover Menu
// @namespace    https://www.tampermonkey.net/
// @version      2026227
// @description  YouTubeロゴにホバーすると、履歴、後で見る、再生リスト、高評価動画に素早くアクセスできるメニューを表示します。
// @description:en Adds a hover menu to the YouTube logo for quick access to history, watch later, playlists, and liked videos.
// @author       black lagoon
// @match        https://www.youtube.com/*
// @grant        GM_addStyle
// @license      MPL2.0
// ==/UserScript==

(function() {
    'use strict';

    const isJA = navigator.language.startsWith('ja');

    const MENU_ITEMS = [
        {
            id: 'history',
            label: isJA ? '履歴' : 'History',
            path: '/feed/history',
            d: 'M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z'
        },
        {
            id: 'watch-later',
            label: isJA ? '後で見る' : 'Watch Later',
            path: '/playlist?list=WL',
            d: 'M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm4.2 14.2L11 13V7h1.5v5.2l4.5 2.7-.8 1.3z'
        },
        {
            id: 'playlists',
            label: isJA ? '再生リスト' : 'Playlists',
            path: '/feed/library',
            d: 'M22,7H2v1h20V7z M13,12H2v-1h11V12z M13,16H2v-1h11V16z M15,19v-8l7,4L15,19z'
        },
        {
            id: 'liked',
            label: isJA ? '高評価' : 'Liked',
            path: '/playlist?list=LL',
            d: 'M18.77,11h-4.23l1.52-4.94C16.38,5.03,15.54,4,14.38,4c-0.58,0-1.13,0.24-1.53,0.65L7,11H3v10h4h1h9.43 c1.06,0,1.98-0.67,2.26-1.68l1.29-4.73C21.35,13.41,20.41,11,18.77,11z M7,20H4v-8h3V20z M19.98,14.37l-1.29,4.73 C18.59,19.64,18.02,20,17.43,20H8v-8.61l5.83-5.97c0.15-0.15,0.35-0.23,0.55-0.23c0.41,0,0.72,0.37,0.6,0.77L13.46,11h1.08h4.23 c0.54,0,0.85,0.79,0.65,1.29L19.98,14.37z'
        }
    ];

    // --- スタイル適用 ---
    const buildIconStyle = (d) => {
        const svg = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'><path d='${d}'/></svg>`;
        return `url("data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}")`;
    };

    GM_addStyle(`
        #yt-hover-quick-menu {
            position: fixed;
            top: 48px;
            left: 16px;
            background-color: var(--yt-spec-menu-background) !important;
            border: 1px solid var(--yt-spec-10-percent-layer) !important;
            border-radius: 12px !important;
            box-shadow: 0 8px 24px rgba(0,0,0,0.2) !important;
            z-index: 2147483647;
            display: none;
            flex-direction: column;
            width: 220px;
            padding: 8px 0;
        }
        .yt-menu-link {
            display: flex !important;
            align-items: center !important;
            padding: 10px 16px 10px 52px !important;
            color: var(--yt-spec-text-primary) !important;
            text-decoration: none !important;
            font-size: 14px !important;
            font-family: "Roboto", "Arial", sans-serif !important;
            position: relative;
            transition: background 0.1s;
        }
        .yt-menu-link:hover {
            background-color: var(--yt-spec-10-percent-layer) !important;
        }
        .yt-menu-link::before {
            content: '';
            position: absolute;
            left: 16px;
            top: 50%;
            transform: translateY(-50%);
            width: 24px;
            height: 24px;
            background-size: contain;
            background-repeat: no-repeat;
            filter: var(--yt-spec-icon-inactive-contrast-filter) !important;
        }
        ${MENU_ITEMS.map(item => `
            .icon-${item.id}::before { background-image: ${buildIconStyle(item.d)}; }
        `).join('\n')}
    `);

    // --- メニュー生成 ---
    const menu = document.createElement('div');
    menu.id = 'yt-hover-quick-menu';

    MENU_ITEMS.forEach(item => {
        const a = document.createElement('a');
        a.href = item.path;
        a.className = `yt-menu-link icon-${item.id}`;
        a.textContent = item.label;
        menu.appendChild(a);
    });
    document.body.appendChild(menu);

    const handleMouseOver = (e) => {
        const logo = document.querySelector('ytd-topbar-logo-renderer');
        // ロゴコンテナ内、もしくはメニュー自体へのホバーを判定
        if ((logo && logo.contains(e.target))) {
            menu.style.display = 'flex';
        } else if (!menu.contains(e.target)) {
            menu.style.display = 'none';
        }
    };

    document.addEventListener('mouseover', handleMouseOver, { passive: true });
    menu.addEventListener('mouseleave', () => menu.style.display = 'none');

})();