Gemini_Userscript_Refactor-Helper

Kombiniert Clean-HTML Export mit einem Profi-Element-Picker (Blockiert Links, findet ähnliche Elemente).

Verzia zo dňa 20.11.2025. Pozri najnovšiu verziu.

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

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 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          Gemini_Userscript_Refactor-Helper
// @namespace     http://tampermonkey.net/
// @version       3.0
// @description   Kombiniert Clean-HTML Export mit einem Profi-Element-Picker (Blockiert Links, findet ähnliche Elemente).
// @author        Dein Vibe Coding Partner
// @match         *://*/*
// @license MIT 
// @run-at        document-end
// @grant         GM_addStyle
// @grant         GM_setClipboard
// @grant         unsafeWindow
// ==/UserScript==

(function() {
    'use strict';

    // Globale Variablen für den Picker
    let isPicking = false;
    let selectedElements = new Set(); // Speichert die tatsächlich geklickten Elemente

    // #############################################################################
    // # TEIL 1: CLEAN HTML GENERATOR (Das Futter für die KI)
    // #############################################################################

    function getCleanHTML() {
        const clone = document.documentElement.cloneNode(true);

        // Müll entfernen
        const tagsToRemove = ['script', 'style', 'noscript', 'iframe', 'svg', 'meta', 'link', 'path', 'symbol', 'img', 'video', 'picture', 'source', 'canvas', 'div[id^="ad-"]'];
        tagsToRemove.forEach(tag => {
            clone.querySelectorAll(tag).forEach(el => el.remove());
        });

        // Kommentare entfernen
        const removeComments = (node) => {
            const children = node.childNodes;
            for (let i = children.length - 1; i >= 0; i--) {
                const child = children[i];
                if (child.nodeType === 8) node.removeChild(child);
                else if (child.nodeType === 1) removeComments(child);
            }
        };
        removeComments(clone);

        // Attribute putzen & Text zu "K"
        const allowedAttrs = ['id', 'class', 'name', 'role', 'type', 'data-test', 'href', 'value'];
        clone.querySelectorAll('*').forEach(el => {
            [...el.attributes].forEach(attr => {
                if (!allowedAttrs.includes(attr.name)) el.removeAttribute(attr.name);
            });
            // Text durch K ersetzen (Skelett-Modus)
            el.childNodes.forEach(node => {
                if (node.nodeType === 3 && node.nodeValue.trim().length > 0) {
                    node.nodeValue = 'K';
                }
            });
        });

        let htmlString = clone.outerHTML.replace(/^\s*[\r\n]/gm, '');
        return `\n${htmlString}\n`;
    }

    function downloadCleanHTML() {
        const htmlContent = getCleanHTML();
        const filename = `${window.location.hostname.replace(/[^a-z0-9]/gi, '_')}_clean.html`;
        
        const blob = new Blob([htmlContent], { type: 'text/html' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        showFeedback(`Datei '${filename}' heruntergeladen!`);
    }

    // #############################################################################
    // # TEIL 2: INTELLIGENTER ELEMENT PICKER
    // #############################################################################

    function togglePicker() {
        isPicking = !isPicking;
        const btn = document.getElementById('gemini-pick-btn');
        const body = document.body;

        if (isPicking) {
            btn.textContent = '✅ Fertig & Kopieren';
            btn.style.background = '#ff6b6b'; // Rot zum Stoppen
            
            // Event Listener HINZUFÜGEN (Capture Phase um Links zu blockieren!)
            document.addEventListener('mouseover', onHover, true);
            document.addEventListener('mouseout', onHoverOut, true);
            document.addEventListener('click', onClick, true);
            
            showFeedback("Pick-Modus AKTIV. Links sind deaktiviert.");
        } else {
            // Beenden und Report erstellen
            finishPicking();
            
            btn.textContent = '🎯 Element Picken';
            btn.style.background = '#77dd77'; // Grün zum Starten
            
            // Event Listener ENTFERNEN
            document.removeEventListener('mouseover', onHover, true);
            document.removeEventListener('mouseout', onHoverOut, true);
            document.removeEventListener('click', onClick, true);
            
            // Aufräumen
            document.querySelectorAll('.gemini-selected, .gemini-similar, .gemini-hover').forEach(el => {
                el.classList.remove('gemini-selected', 'gemini-similar', 'gemini-hover');
            });
            selectedElements.clear();
        }
    }

    function onHover(e) {
        if (!isPicking || e.target.closest('#gemini-panel')) return;
        e.stopPropagation();
        e.target.classList.add('gemini-hover');
    }

    function onHoverOut(e) {
        if (!isPicking) return;
        e.target.classList.remove('gemini-hover');
    }

    function onClick(e) {
        if (!isPicking || e.target.closest('#gemini-panel')) return;
        
        // KILLT ALLE KLICKS AUF DER SEITE (Links, Buttons, etc.)
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();

        const el = e.target;

        // Toggle Auswahl
        if (selectedElements.has(el)) {
            el.classList.remove('gemini-selected');
            selectedElements.delete(el);
            // Ähnliche entfernen (einfachheitshalber alle ähnlichen resetten und neu berechnen)
            document.querySelectorAll('.gemini-similar').forEach(sim => sim.classList.remove('gemini-similar'));
        } else {
            el.classList.add('gemini-selected');
            selectedElements.add(el);
            highlightSimilar(el);
        }
    }

    // Findet Elemente, die genauso aussehen (gleiche Klassen)
    function highlightSimilar(el) {
        if (!el.className) return; // Ohne Klassen zu riskant
        
        // Wir bauen einen Selektor aus dem Tag und den Klassen
        const classes = Array.from(el.classList)
            .filter(c => !c.startsWith('gemini-')) // Unsere eigenen Klassen ignorieren
            .join('.');
            
        if (!classes) return;

        const selector = `${el.tagName.toLowerCase()}.${classes}`;
        
        // Alle finden
        try {
            const similar = document.querySelectorAll(selector);
            similar.forEach(sim => {
                if (sim !== el && !selectedElements.has(sim)) {
                    sim.classList.add('gemini-similar');
                }
            });
            showFeedback(`${similar.length} ähnliche Elemente gefunden!`);
        } catch (err) {
            // Manchmal sind Klassen ungültige Selektoren (z.B. mit Doppelpunkt), ignorieren wir.
        }
    }

    function finishPicking() {
        if (selectedElements.size === 0) {
            showFeedback("Nichts ausgewählt.");
            return;
        }

        let report = "### GEMINI ELEMENT REPORT ###\n\n";

        selectedElements.forEach(el => {
            const styles = window.getComputedStyle(el);
            
            // Versuchen, einen eindeutigen Selektor zu bauen
            let selector = el.tagName.toLowerCase();
            if (el.id) selector += `#${el.id}`;
            if (el.className) {
                 const cleanClasses = Array.from(el.classList)
                    .filter(c => !c.startsWith('gemini-'))
                    .join('.');
                 if (cleanClasses) selector += `.${cleanClasses}`;
            }

            report += `ITEM: ${selector}\n`;
            report += `--------------------------------------------------\n`;
            report += `INNER TEXT: "${el.innerText.substring(0, 50)}..."\n`;
            report += `HTML (Outer): \n${el.outerHTML.replace(/gemini-[a-z]+\s?/g, '')}\n`; // Unsere Klassen aus HTML entfernen
            report += `COMPUTED STYLES:\n`;
            report += `- Display: ${styles.display}\n`;
            report += `- Color: ${styles.color}\n`;
            report += `- Background: ${styles.backgroundColor}\n`;
            report += `- Font: ${styles.fontSize} ${styles.fontFamily}\n`;
            report += `- Padding/Margin: ${styles.padding} / ${styles.margin}\n`;
            report += `\n\n`;
        });

        GM_setClipboard(report);
        showFeedback(`${selectedElements.size} Elemente kopiert!`);
    }

    // #############################################################################
    // # UI & CSS
    // #############################################################################

    function showFeedback(text) {
        const fb = document.getElementById('gemini-feedback');
        fb.innerText = text;
        fb.classList.add('visible');
        setTimeout(() => fb.classList.remove('visible'), 3000);
    }

    function createUI() {
        const panel = document.createElement('div');
        panel.id = 'gemini-panel';
        
        const btnClean = document.createElement('button');
        btnClean.innerHTML = '💀 <b>Clean HTML</b> (Skelett)';
        btnClean.onclick = downloadCleanHTML;
        
        const btnPick = document.createElement('button');
        btnPick.id = 'gemini-pick-btn';
        btnPick.innerHTML = '🎯 <b>Element Picken</b>';
        btnPick.onclick = togglePicker;

        const feedback = document.createElement('div');
        feedback.id = 'gemini-feedback';

        panel.appendChild(btnClean);
        panel.appendChild(btnPick);
        document.body.appendChild(panel);
        document.body.appendChild(feedback);
    }

    GM_addStyle(`
        /* GUI Styles */
        #gemini-panel {
            position: fixed; bottom: 20px; right: 20px; z-index: 2147483647;
            display: flex; gap: 10px; background: #222; padding: 10px;
            border-radius: 50px; box-shadow: 0 5px 20px rgba(0,0,0,0.5);
            border: 1px solid #444;
        }
        #gemini-panel button {
            background: #444; color: white; border: none; padding: 10px 20px;
            border-radius: 30px; cursor: pointer; font-family: sans-serif; font-size: 14px;
            transition: all 0.2s;
        }
        #gemini-panel button:hover { transform: scale(1.05); filter: brightness(1.2); }
        
        #gemini-feedback {
            position: fixed; bottom: 80px; right: 20px; z-index: 2147483647;
            background: #333; color: #fff; padding: 10px 20px; border-radius: 5px;
            font-family: sans-serif; opacity: 0; transition: opacity 0.3s; pointer-events: none;
        }
        #gemini-feedback.visible { opacity: 1; }

        /* HIGHLIGHT STYLES */
        .gemini-hover {
            outline: 2px dashed #ff00ff !important;
            cursor: crosshair !important;
        }
        .gemini-selected {
            outline: 3px solid #0044ff !important; /* Dunkelblau für das Gewählte */
            background-color: rgba(0, 68, 255, 0.2) !important;
            box-shadow: 0 0 10px rgba(0, 68, 255, 0.5) !important;
        }
        .gemini-similar {
            outline: 2px dotted #00ccff !important; /* Hellblau für ähnliche */
            background-color: rgba(0, 204, 255, 0.1) !important;
        }
    `);

    createUI();

})();