Grok Alternador de Arquivos e Postagens

Alterne entre suas postagens do Grok imagine e links diretos de arquivos.

Terá de instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

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

Terá de instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Terá de instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Terá de instalar uma extensão como Tampermonkey para instalar este script.

Terá de instalar uma extensão de gestão de scripts de utilizador para instalar este script.

(Já tenho um gestor de scripts de utilizador, deixe-me instalá-lo!)

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

(Já tenho um gestor de estilos de utilizador, deixe-me instalá-lo!)

// ==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.3.0
// @author          Wack.3gp (https://greasyfork.org/users/4792)
// @copyright       2026+, Wack.3gp
// @namespace       https://greasyfork.org/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://greasyfork.org/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 extractIdFromString(str) {
        if (!str) return null;
        let match = str.match(/\/generated\/([a-z0-9-]{36})/i) || str.match(/\\\/generated\\\/([a-z0-9-]{36})/i);
        if (!match) {
            match = str.match(/\/users\/[^\/]+\/([a-z0-9-]{36})/i) || str.match(/\\\/users\\\/[^\/]+\\\/([a-z0-9-]{36})/i);
        }
        return match ? match[1] : null;
    }

    function switchToPost() {
        const modal = document.querySelector('[role="dialog"], div[class*="fixed"][class*="z-"]');
        if (modal) {
            const modalMedia = modal.querySelector('video, img[src*="assets.grok.com"]');
            if (modalMedia && modalMedia.src) {
                const id = extractIdFromString(modalMedia.src);
                if (id) {
                    window.open(postPath + id, "_blank");
                    return;
                }
            }

            const reactKey = Object.keys(modal).find(k => k.startsWith('__reactProps') || k.startsWith('__reactFiber'));
            if (reactKey) {
                try {
                    const propsStr = JSON.stringify(modal[reactKey]);
                    const id = extractIdFromString(propsStr);
                    if (id) {
                        window.open(postPath + id, "_blank");
                        return;
                    }
                } catch(e) {}
            }
        }

        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) {
            const id = extractIdFromString(mediaElement.src);
            if (id) {
                window.open(postPath + id, "_blank");
                return;
            }
        }

        const activeElement = document.activeElement || document.querySelector(':hover');
        if (activeElement) {
            const item = activeElement.closest('div.cursor-pointer') || activeElement.closest('a');
            if (item) {
                const reactKey = Object.keys(item).find(k => k.startsWith('__reactProps'));
                if (reactKey) {
                    try {
                        const propsStr = JSON.stringify(item[reactKey]);
                        const id = extractIdFromString(propsStr);
                        if (id) {
                            window.open(postPath + id, "_blank");
                            return;
                        }
                    } catch(e) {}
                }
            }
        }

        console.warn("Switcher: Keine gültige Post-ID für das geöffnete File gefunden.");
    }

    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();
})();