Pixiv Popular Tags Artwork Shortcut

Adds a direct shortcut button to artwork thumnail from the 'Popular Illust Tags' dropdown. Also reveals all hidden illust tags while hiding novels. Features are toggleable.

// ==UserScript==
// @name         Pixiv Popular Tags Artwork Shortcut
// @name:ko      픽시브 인기 일러스트 태그 바로가기
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Adds a direct shortcut button to artwork thumnail from the 'Popular Illust Tags' dropdown. Also reveals all hidden illust tags while hiding novels. Features are toggleable.
// @description:ko  픽시브 검색창 '인기 일러스트 태그' 썸네일에 아트워크 바로가기 버튼을 추가합니다. 또한 '인기 소설 태그' 자리에 대신 숨겨진 일러스트 태그를 모두 표시합니다. (토글 가능)
// @author       YourName
// @match        https://www.pixiv.net/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @icon         https://www.google.com/s2/favicons?sz=64&domain=pixiv.net
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    console.log('[Pixiv Popular Tags Enhancer] Script v2.0 starting.');

    // --- Localization ---
    const lang = navigator.language.slice(0, 2); // 'ko', 'en', etc.

    const localizations = {
        ko: {
            menuLabel: "숨겨진 일러스트 태그 보이기 (현재: {status})",
            alertMessage: "숨겨진 일러스트 태그 보이기 기능이 [{status}] 되었습니다.\n다음에 검색창을 열 때부터 적용됩니다.",
            statusOn: "ON",
            statusOff: "OFF",
            statusEnabled: "활성화",
            statusDisabled: "비활성화"
        },
        en: {
            menuLabel: "Toggle 'Reveals all hidden illust tags' Feature (Current: {status})",
            alertMessage: "The 'Reveals all hidden illust tags' feature is now [{status}].\nThis will apply next time you open the search box.",
            statusOn: "ON",
            statusOff: "OFF",
            statusEnabled: "ENABLED",
            statusDisabled: "DISABLED"
        }
    };

    // Select the appropriate language strings, defaulting to English
    const T = localizations[lang] || localizations.en;

    // --- Settings Management ---
    let revealSetting = GM_getValue('revealHiddenArtworks', true);
    let menuCommandId = null;

    function updateMenuCommand() {
        // Unregister the previous menu command if it exists
        if (menuCommandId) {
            GM_unregisterMenuCommand(menuCommandId);
        }

        const menuStatus = revealSetting ? T.statusOn : T.statusOff;
        const menuText = T.menuLabel.replace('{status}', menuStatus);

        menuCommandId = GM_registerMenuCommand(menuText, () => {
            revealSetting = !revealSetting;
            GM_setValue('revealHiddenArtworks', revealSetting);

            const alertStatus = revealSetting ? T.statusEnabled : T.statusDisabled;
            const alertText = T.alertMessage.replace('{status}', alertStatus);
            alert(alertText);

            // Re-register the menu command to update the label (e.g., ON -> OFF)
            updateMenuCommand();
        });
    }

    // Initial registration of the menu command
    updateMenuCommand();
    // --- End of Settings Management ---

    GM_addStyle(`
        div.gtm-search-box-popular-artwork > a { position: relative !important; }
        .artwork-link-button {
            position: absolute; top: 6px; left: 6px; z-index: 100;
            background-color: rgba(0, 0, 0, 0.65); color: white !important;
            border: none; border-radius: 5px; padding: 2px 6px;
            font-size: 14px; font-weight: bold; text-decoration: none !important;
            cursor: pointer; opacity: 0.8; transition: opacity 0.2s ease-in-out;
            line-height: 1;
        }
        .artwork-link-button:hover { opacity: 1; color: white !important; }
    `);

    let checkInterval = null;
    let checkTimeout = null;

    function addArtworkLinks(suggestionContainer) {
        // The rest of your script logic remains the same.
        // ... (이하 스크립트 로직은 동일) ...
        console.log('[Pixiv Popular Tags Enhancer] Starting to add artwork buttons.');
        const popularTagDivs = suggestionContainer.querySelectorAll('div.gtm-search-box-popular-artwork');

        if (revealSetting && popularTagDivs.length > 10) {
            const eleventhThumbnail = popularTagDivs[10];
            const hiddenArtworkContainer = eleventhThumbnail.closest('div.hidden');

            if (hiddenArtworkContainer) {
                console.log('[Pixiv Popular Tags Enhancer] Found hidden artwork container. Removing "hidden" class.');
                hiddenArtworkContainer.classList.remove('hidden');

                const parent = hiddenArtworkContainer.parentNode;
                if (parent) {
                    for (const sibling of parent.children) {
                        if (sibling === hiddenArtworkContainer) continue;

                        if (sibling.querySelector('div.gtm-search-box-popular-novel')) {
                            console.log('[Pixiv Popular Tags Enhancer] Successfully identified novel container. Adding "hidden" class.', sibling);
                            sibling.classList.add('hidden');
                            break;
                        }
                    }
                }
            }
        }

        popularTagDivs.forEach(thumbnailDiv => {
            const linkElement = thumbnailDiv.closest('a');
            if (!linkElement || linkElement.querySelector('.artwork-link-button')) return;

            linkElement.style.position = 'relative';
            const idSpan = thumbnailDiv.querySelector('span[data-gtm-value]');

            if (idSpan) {
                const artworkId = idSpan.getAttribute('data-gtm-value');
                if (artworkId) {
                    const button = document.createElement('a');
                    button.href = `https://www.pixiv.net/artworks/${artworkId}`;
                    button.textContent = '🎨';
                    button.title = `Go to artwork: ${artworkId}`;
                    button.className = 'artwork-link-button';
                    button.target = '_blank';
                    button.rel = 'noopener noreferrer';
                    button.addEventListener('click', e => e.stopPropagation());
                    linkElement.appendChild(button);
                }
            }
        });

        suggestionContainer.setAttribute('data-artwork-buttons-added', 'true');
        console.log('[Pixiv Popular Tags Enhancer] Button addition complete. Stopping polling.');
    }

    function stopPolling() {
        if (checkInterval) {
            clearInterval(checkInterval);
            checkInterval = null;
        }
        if (checkTimeout) {
            clearTimeout(checkTimeout);
            checkTimeout = null;
        }
    }

    function startPollingForThumbnails(suggestionContainer) {
        if (checkInterval) return;

        console.log('[Pixiv Popular Tags Enhancer] Waiting for popular tags to load (starting polling).');
        const maxWaitTime = 5000;
        const intervalTime = 200;

        checkInterval = setInterval(() => {
            const popularTagDivs = suggestionContainer.querySelectorAll('div.gtm-search-box-popular-artwork');
            if (popularTagDivs.length > 0) {
                console.log(`[Pixiv Popular Tags Enhancer] Found ${popularTagDivs.length} popular tags!`);
                stopPolling();
                addArtworkLinks(suggestionContainer);
            }
        }, intervalTime);

        checkTimeout = setTimeout(() => {
            if (checkInterval) {
                console.warn('[Pixiv Popular Tags Enhancer] Could not find popular tags within 5 seconds. Stopping poll.');
                stopPolling();
            }
        }, maxWaitTime);
    }

    const observerCallback = (mutationsList) => {
        const suggestionContainer = document.querySelector('div[class*="sc-99e0b92e-0"][open]');
        if (suggestionContainer) {
            if (!suggestionContainer.hasAttribute('data-artwork-buttons-added') && !checkInterval) {
                startPollingForThumbnails(suggestionContainer);
            }
        } else {
            stopPolling();
        }
    };

    const observer = new MutationObserver(observerCallback);
    observer.observe(document.body, { childList: true, subtree: true });

})();