// ==UserScript==
// @name Universal Component Analyzer for LLM
// @namespace http://tampermonkey.net/
// @version 16.0
// @description Analysiert JEDES Element auf JEDER Webseite. Zuverlässig & mit hochkomprimierter Ausgabe für LLMs.
// @author Assistant & Dein Name
// @match *://*/*
// @grant GM_addStyle
// @license MIT
// @grant GM_setClipboard
// ==/UserScript==
(function() {
'use strict';
// Globale Variablen & Cache für Performance
let pickerMode = false, pickerButton = null, lastHoveredElement = null, selectedElements = [];
let isDragging = false, wasDragging = false, offsetX, offsetY;
const defaultStylesCache = new Map();
// CSS-Stile für das Skript
GM_addStyle(`
#element-picker-btn { position: fixed; right: 20px; top: 50%; transform: translateY(-50%); width: 50px; height: 50px; background: #007185; border: none; border-radius: 50%; cursor: grab; z-index: 999999; box-shadow: 0 2px 10px rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center; transition: background-color 0.3s ease; }
#element-picker-btn:hover { background-color: #005a6b; }
body.picker-dragging, body.picker-dragging * { cursor: grabbing !important; }
#element-picker-btn.active { background: #dc3545; animation: pulse 1s infinite; }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7); } 70% { box-shadow: 0 0 0 10px rgba(220, 53, 69, 0); } 100% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0); } }
.picker-cursor, .picker-cursor * { cursor: crosshair !important; }
.picker-highlight { outline: 2px solid #00A896 !important; outline-offset: 1px !important; background-color: rgba(0, 168, 150, 0.1) !important; }
.picker-selected { outline: 2px solid #007bff !important; outline-offset: 1px !important; background-color: rgba(0, 123, 255, 0.15) !important; }
.picker-notification { position: fixed; top: 20px; right: 20px; background: #28a745; color: white; padding: 10px 20px; border-radius: 5px; z-index: 1000000; font-family: Arial, sans-serif; font-size: 14px; animation: slideIn 0.3s ease-out; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
.picker-status { position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: #dc3545; color: white; padding: 8px 16px; border-radius: 5px; z-index: 1000000; font-family: Arial, sans-serif; font-size: 12px; font-weight: bold; }
`);
// --- KERNFUNKTIONEN (FINAL KORRIGIERT) ---
function onMouseOver(e) {
if (!pickerMode) return;
const targetElement = e.target;
if (!targetElement || targetElement.id === 'element-picker-btn' || targetElement.closest('#element-picker-btn')) {
if (lastHoveredElement) {
lastHoveredElement.classList.remove('picker-highlight');
lastHoveredElement = null;
}
return;
}
if (lastHoveredElement !== targetElement) {
if (lastHoveredElement) {
lastHoveredElement.classList.remove('picker-highlight');
}
lastHoveredElement = targetElement;
lastHoveredElement.classList.add('picker-highlight');
}
e.stopPropagation();
}
// **DER FIX:** Diese Funktion hat gefehlt und den Absturz verursacht.
function onMouseOut(e) {
// Diese Funktion ist hauptsächlich zur Stabilität da.
// Die Logik zum Entfernen des Highlights wird von onMouseOver gehandhabt.
e.stopPropagation();
}
function onElementClick(e) {
if (!pickerMode || e.target.id === 'element-picker-btn' || e.target.closest('#element-picker-btn')) return;
e.preventDefault();
e.stopPropagation();
const selectedElement = lastHoveredElement;
if (!selectedElement) return;
if (e.ctrlKey) { // Multi-Pick
const index = selectedElements.indexOf(selectedElement);
if (index > -1) {
selectedElement.classList.remove('picker-selected');
selectedElements.splice(index, 1);
} else {
selectedElement.classList.add('picker-selected');
selectedElements.push(selectedElement);
}
showStatus(`${selectedElements.length} Element(e) ausgewählt. Klick ohne STRG zum Kopieren.`);
} else { // Normaler Klick
let elementsToCopy = [];
if (selectedElements.length > 0 && !selectedElements.includes(selectedElement)) {
selectedElements.push(selectedElement);
elementsToCopy = selectedElements;
} else if (selectedElements.length > 0) {
elementsToCopy = selectedElements;
} else {
elementsToCopy = [selectedElement];
}
if (elementsToCopy.length > 0) {
let combinedText = "";
elementsToCopy.forEach((element, i) => {
combinedText += extractGenericElementData(element);
if (i < elementsToCopy.length - 1) { combinedText += '\n\n\n'; }
});
GM_setClipboard(combinedText, 'text');
showNotification(`${elementsToCopy.length} Element-Blaupause(n) kopiert!`);
}
deactivatePickerMode();
}
}
function activatePickerMode() {
pickerButton.classList.add('active');
document.body.classList.add('picker-cursor');
showStatus('Picker aktiv. STRG+Klick für Mehrfachauswahl. ESC zum Beenden.');
document.addEventListener('mouseover', onMouseOver, true);
document.addEventListener('mouseout', onMouseOut, true); // **DER FIX:** Die Funktion onMouseOut existiert jetzt.
document.addEventListener('click', onElementClick, true);
document.addEventListener('keydown', onKeyDown, true);
}
// --- DATENEXTRAKTION (unverändert) ---
function getCompressedCss(element) { const hadHighlight = element.classList.contains('picker-highlight'); if (hadHighlight) element.classList.remove('picker-highlight'); const tagName = element.tagName; if (!defaultStylesCache.has(tagName)) { const dummy = document.createElement(tagName); document.body.appendChild(dummy); defaultStylesCache.set(tagName, window.getComputedStyle(dummy)); document.body.removeChild(dummy); } const styles = window.getComputedStyle(element); const defaultStyles = defaultStylesCache.get(tagName); const cssProps = []; const relevantProperties = [ 'display', 'position', 'flex-direction', 'justify-content', 'align-items', 'width', 'height', 'margin', 'padding', 'border', 'border-radius', 'font-weight', 'color' ]; for (const prop of relevantProperties) { const value = styles.getPropertyValue(prop); if (value !== defaultStyles.getPropertyValue(prop)) { cssProps.push(`${prop}: ${value}`); } } if (hadHighlight) element.classList.add('picker-highlight'); return cssProps.length > 0 ? `{ ${cssProps.join('; ')} }` : ''; }
function buildChildTree(element, level = 0) { let treeText = ''; const indentation = ' '.repeat(level); for (const child of element.children) { if (child.tagName === 'SCRIPT') continue; const childCss = getCompressedCss(child); const classAttr = child.className ? ` class="${child.className}"` : ''; treeText += `${indentation}└─ <${child.tagName.toLowerCase()}${classAttr}> ${childCss}\n`; if (child.children.length > 0) { treeText += buildChildTree(child, level + 1); } } return treeText; }
function extractGenericElementData(selectedElement) { const childTree = buildChildTree(selectedElement); let output = '----------------ELEMENT BLUEPRINT----------------\n'; output += `SOURCE: ${window.location.href}\n`; output += `SELECTED ELEMENT: <${selectedElement.tagName.toLowerCase()}` + (selectedElement.className ? ` class="${selectedElement.className}"` : '') + (selectedElement.id ? ` id="${selectedElement.id}"` : '') + '>\n\n'; output += '---STRUCTURE & STYLES---\n'; output += childTree || '(No child elements found)\n\n'; output += '---RAW HTML---\n'; output += compactHTML(selectedElement.outerHTML); output += '\n----------------END----------------'; return output; }
// --- HILFSFUNKTIONEN (unverändert) ---
function makeButtonDraggable(button) { button.addEventListener('mousedown', (e) => { if (e.button !== 0 || pickerMode) return; isDragging = true; wasDragging = false; document.body.classList.add('picker-dragging'); button.style.cursor = 'grabbing'; button.style.transition = 'none'; const rect = button.getBoundingClientRect(); button.style.left = `${rect.left}px`; button.style.top = `${rect.top}px`; button.style.right = 'auto'; button.style.transform = 'none'; offsetX = e.clientX - rect.left; offsetY = e.clientY - rect.top; document.addEventListener('mousemove', onDragMove); document.addEventListener('mouseup', onDragEnd); }); function onDragMove(e) { if (!isDragging) return; e.preventDefault(); wasDragging = true; let newX = e.clientX - offsetX; let newY = e.clientY - offsetY; newX = Math.max(0, Math.min(newX, window.innerWidth - button.offsetWidth)); newY = Math.max(0, Math.min(newY, window.innerHeight - button.offsetHeight)); button.style.left = `${newX}px`; button.style.top = `${newY}px`; } function onDragEnd() { if (!isDragging) return; isDragging = false; document.body.classList.remove('picker-dragging'); button.style.cursor = 'grab'; button.style.transition = 'background-color 0.3s ease'; document.removeEventListener('mousemove', onDragMove); document.removeEventListener('mouseup', onDragEnd); if (wasDragging) { const position = { x: button.style.left, y: button.style.top }; localStorage.setItem('pickerButtonPosition', JSON.stringify(position)); } } }
function togglePickerMode() { if (wasDragging) { wasDragging = false; return; } pickerMode = !pickerMode; if (pickerMode) { activatePickerMode(); } else { deactivatePickerMode(); } }
function deactivatePickerMode() { pickerMode = false; pickerButton.classList.remove('active'); document.body.classList.remove('picker-cursor'); if (lastHoveredElement) { lastHoveredElement.classList.remove('picker-highlight'); } selectedElements.forEach(el => el.classList.remove('picker-selected')); selectedElements = []; lastHoveredElement = null; removeStatus(); document.removeEventListener('mouseover', onMouseOver, true); document.removeEventListener('mouseout', onMouseOut, true); document.removeEventListener('click', onElementClick, true); document.removeEventListener('keydown', onKeyDown, true); }
function onKeyDown(e) { if (pickerMode && e.key === 'Escape') { e.preventDefault(); deactivatePickerMode(); } }
function createPipetteIcon() { const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("width", "24"); svg.setAttribute("height", "24"); svg.setAttribute("viewBox", "0 0 24 24"); svg.setAttribute("fill", "white"); const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("d", "M20.71 5.63l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-3.12 3.12-1.93-1.91-1.41 1.41 1.42 1.42L3 16.25V21h4.75l8.92-8.92 1.42 1.42 1.41-1.41-1.91-1.93 3.12-3.12c.4-.4.4-1.03 0-1.41zM6.92 19L5 17.08l8.06-8.06 1.92 1.92L6.92 19z"); svg.appendChild(path); return svg; }
function createPickerButton() { const button = document.createElement('button'); button.id = 'element-picker-btn'; button.title = 'Komponenten-Analysator starten'; button.appendChild(createPipetteIcon()); button.addEventListener('click', togglePickerMode); document.body.appendChild(button); return button; }
function loadButtonPosition() { const savedPosition = localStorage.getItem('pickerButtonPosition'); if (savedPosition) { const pos = JSON.parse(savedPosition); pickerButton.style.top = pos.y; pickerButton.style.left = pos.x; pickerButton.style.right = 'auto'; pickerButton.style.transform = 'none'; } }
function compactHTML(html) { return html.replace(/\s+/g, ' ').replace(/\s*<\s*/g, '<').replace(/\s*>\s*/g, '>').trim(); }
function showNotification(message) { const n=document.createElement('div');n.className='picker-notification';n.textContent=message;document.body.appendChild(n);setTimeout(()=>{if(n.parentNode)n.parentNode.removeChild(n)},3000); }
function showStatus(message) { removeStatus();const s=document.createElement('div');s.id='picker-status';s.className='picker-status';s.textContent=message;document.body.appendChild(s); }
function removeStatus() { const e=document.getElementById('picker-status');if(e)e.parentNode.removeChild(e); }
function init() { pickerButton = createPickerButton(); makeButtonDraggable(pickerButton); loadButtonPosition(); console.log('Universal Component Analyzer (FINAL & WORKING) wurde geladen.'); }
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); }
})();