Gemini_Userscript_Refactor-Helper

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

2025-11-20 기준 버전입니다. 최신 버전을 확인하세요.

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

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

이 스크립트를 설치하려면 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();

})();