YouTube Logo Hover Menu

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

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

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

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

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.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==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');

})();