Grok File & Post Switcher

Switch between your Grok imagine posts and direct file links.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name            Grok File & Post Switcher
// @name:de         Grok Datei & Post Wechsler
// @name:fr         Grok Commutateur de Fichiers & Publications
// @name:es         Grok Cambiador de Archivos y Publicaciones
// @name:it         Grok Scambiatore di File e Post
// @name:pt         Grok Alternador de Arquivos e Postagens
// @description     Switch between your Grok imagine posts and direct file links.
// @description:de  Wechselt zwischen deinen Grok Imagine-Beiträgen und direkten Dateilinks.
// @description:fr  Basculez zwischen vos publications Grok imagine et les liens directs vers les fichiers.
// @description:es  Cambie entre sus publicaciones de Grok imagine y los enlaces directos a los archivos.
// @description:it  Passa tra i tuoi post Grok imagine e i link diretti ai file.
// @description:pt  Alterne entre suas postagens do Grok imagine e links diretos de arquivos.
// @version         2.2.9-reworking
// @author          Wack.3gp (https://greatest.deepsurf.us/users/4792)
// @copyright       2026+, Wack.3gp
// @namespace       https://greatest.deepsurf.us/users/4792
// @license         CC BY-NC-ND 4.0; http://creativecommons.org/licenses/by-nc-nd/4.0/
// @icon            https://grok.com/images/favicon.ico
//
// @match           https://grok.com/*
// @noframes
// @run-at          document-idle
//
// @grant           GM_registerMenuCommand
// @grant           GM_info
// @grant           GM_setValue
// @grant           GM_getValue
// @grant           GM_openInTab
// @grant           GM_deleteValue
//
// @compatible      Chrome tested with Tampermonkey
// @supportURL      https://greatest.deepsurf.us/scripts/574064/feedback
// @contributionURL https://www.paypal.com/donate/?hosted_button_id=BYW9D395KJWZ2
// @contributionAmount €1.00
// ==/UserScript==

(function() {
    'use strict';

    const postPath = "https://grok.com/imagine/post/";
    const filesPage = "https://grok.com/files";

    function getIDFromURL() {
        const url = window.location.href;
        const match = url.match(/\/imagine\/(?:post|saved)\/([a-z0-9-]{36})/);
        return match ? match[1] : null;
    }

    function switchToOriginalFile() {
        const fileId = getIDFromURL();
        if (fileId) {
            GM_setValue("pendingSearchID", fileId);
            window.location.href = filesPage;
        }
    }

    function switchToPost() {
        const video = document.querySelector('video');
        const photo = document.querySelector('img[src*="assets.grok.com"].object-contain, img[src*="assets.grok.com"][class*="object-contain"]');
        const mediaElement = video || photo;

        if (mediaElement && mediaElement.src) {
            let match = mediaElement.src.match(/\/generated\/([^\/]+)\//);
            if (!match) {
                match = mediaElement.src.match(/\/users\/[^\/]+\/([a-z0-9-]{36})/);
            }
            if (match && match[1]) {
                window.open(postPath + match[1], "_blank");
                return;
            }
        }

        const modal = document.querySelector('[role="dialog"]');
        if (modal) {
             const reactKey = Object.keys(modal).find(k => k.startsWith('__reactProps') || k.startsWith('__reactFiber'));
             if (reactKey) {
                 try {
                     const propsStr = JSON.stringify(modal[reactKey]);
                     const idMatch = propsStr.match(/\/generated\/([^\/\\"]+)/) || propsStr.match(/[a-z0-9-]{36}/);
                     if (idMatch) {
                         const id = idMatch[1] || idMatch[0];
                         window.open(postPath + id, "_blank");
                         return;
                     }
                 } catch(e) {}
             }
        }
        console.warn("Switcher: No media ID found.");
    }

    function scanAndClickFile(targetId) {
        const items = Array.from(document.querySelectorAll('div.cursor-pointer'));
        for (const el of items) {
            const reactKey = Object.keys(el).find(k => k.startsWith('__reactProps'));
            if (reactKey) {
                try {
                    if (JSON.stringify(el[reactKey]).includes(targetId)) {
                        el.scrollIntoView({ behavior: 'auto', block: 'center' });
                        setTimeout(() => el.click(), 150);
                        GM_deleteValue("pendingSearchID");
                        return true;
                    }
                } catch(e) {}
            }
        }
        return false;
    }

    function performAutoSearch() {
        const pendingID = GM_getValue("pendingSearchID");
        if (!pendingID || !window.location.href.includes("/files")) return;

        let attempts = 0;
        const searchLoop = setInterval(() => {
            if (scanAndClickFile(pendingID)) {
                clearInterval(searchLoop);
                return;
            }
            const container = document.querySelector('.scroll-feather-y') || document.querySelector('.overflow-y-auto');
            if (container) {
                container.scrollTop += 1200;
                if (container.scrollTop + container.clientHeight >= container.scrollHeight - 50) attempts++;
            }
            if (attempts > 15) {
                clearInterval(searchLoop);
                GM_deleteValue("pendingSearchID");
                console.log("Switcher: ID not found in list.");
            }
        }, 800);
    }

    window.addEventListener('keydown', (e) => {
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
        if (e.key === '^' || e.code === 'Backquote') {
            if (window.location.href.includes("/files")) switchToPost();
            else switchToOriginalFile();
        }
    }, true);

    function init() {
        if (window.location.href.includes("/files")) {
            setTimeout(performAutoSearch, 1000);
        }
        GM_registerMenuCommand("📁 Search Original File", switchToOriginalFile);
        GM_registerMenuCommand("🔗 View Post", switchToPost);
        GM_registerMenuCommand("☕ Buy Me a Coffee :)", function () {
            window.open("https://www.paypal.com/donate/?hosted_button_id=BYW9D395KJWZ2", "_blank");
        });
    }

    init();
})();