Gemini_Userscript_Refactor-Helper

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

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

})();