(() => {
const INTERACTIVE = 'a, button, input, select, textarea, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [onclick], [contenteditable="true"]';
const results = [];
const seen = new Set();
function getLabel(el) {
if (el.id) {
const label = document.querySelector('label[for=' + JSON.stringify(el.id) + ']');
if (label) return label.textContent.trim();
}
const parentLabel = el.closest('label');
if (parentLabel) {
const clone = parentLabel.cloneNode(true);
clone.querySelectorAll('input, select, textarea').forEach(c => c.remove());
const t = clone.textContent.trim();
if (t) return t;
}
const labelledBy = el.getAttribute('aria-labelledby');
if (labelledBy) {
const lbl = document.getElementById(labelledBy);
if (lbl) return lbl.textContent.trim();
}
const prev = el.previousElementSibling;
if (prev && prev.tagName === 'LABEL') return prev.textContent.trim();
return '';
}
function collect(root) {
const all = root.querySelectorAll('*');
for (const node of all) {
if (node.matches(INTERACTIVE)) processElement(node);
if (node.shadowRoot) collect(node.shadowRoot);
}
}
function processElement(el) {
const rect = el.getBoundingClientRect();
if (rect.width < 2 || rect.height < 2) return;
const style = getComputedStyle(el);
if (style.display === 'none' || style.visibility === 'hidden' || parseFloat(style.opacity) < 0.1) return;
if (typeof __eoka_viewport_only !== 'undefined' && __eoka_viewport_only) {
if (rect.bottom < 0 || rect.top > window.innerHeight) return;
if (rect.right < 0 || rect.left > window.innerWidth) return;
}
const tag = el.tagName.toLowerCase();
const isFormEl = tag === 'input' || tag === 'select' || tag === 'textarea';
const inputType = el.getAttribute('type') || '';
let text = el.getAttribute('aria-label') || '';
if (!text) {
if (tag === 'a' || tag === 'button') {
text = (el.textContent || '').trim().replace(/\s+/g, ' ');
if (text.length > 80) text = '';
} else if (isFormEl) {
const label = getLabel(el);
if (label) {
text = label;
} else if (tag === 'select') {
const opt = el.options && el.options[el.selectedIndex];
text = opt ? opt.text : '';
}
} else {
text = (el.textContent || '').trim().replace(/\s+/g, ' ');
}
}
if (text.length > 60) text = text.substring(0, 57) + '...';
const placeholder = el.getAttribute('placeholder') || '';
const ariaLabel = el.getAttribute('aria-label') || '';
const title = el.getAttribute('title') || '';
if (!text && !placeholder && !ariaLabel && !title && !isFormEl) {
return;
}
if ((tag === 'a' || tag === 'button') && el.children.length === 1) {
const child = el.children[0];
const childTag = child.tagName.toLowerCase();
if (childTag === 'button' || childTag === 'input') return;
}
let selector;
if (el.id) {
selector = '#' + CSS.escape(el.id);
} else if (isFormEl && el.name) {
if ((inputType === 'radio' || inputType === 'checkbox') && el.value) {
selector = tag + '[name=' + JSON.stringify(el.name) + '][value=' + JSON.stringify(el.value) + ']';
} else {
selector = tag + '[name=' + JSON.stringify(el.name) + ']';
}
} else if (ariaLabel) {
selector = tag + '[aria-label=' + JSON.stringify(ariaLabel) + ']';
} else if (tag === 'input' && inputType && placeholder) {
selector = 'input[type=' + JSON.stringify(inputType) + '][placeholder=' + JSON.stringify(placeholder) + ']';
} else if (el.getAttribute('data-testid')) {
selector = '[data-testid=' + JSON.stringify(el.getAttribute('data-testid')) + ']';
} else {
const parts = [];
let node = el;
while (node && node !== document.body && parts.length < 4) {
let s = node.tagName.toLowerCase();
if (node.id) {
parts.unshift('#' + CSS.escape(node.id));
break;
}
const parent = node.parentElement;
if (parent) {
const siblings = Array.from(parent.children).filter(c => c.tagName === node.tagName);
if (siblings.length > 1) {
s += ':nth-of-type(' + (siblings.indexOf(node) + 1) + ')';
}
}
parts.unshift(s);
node = parent;
}
selector = parts.join(' > ');
}
if (seen.has(selector)) return;
seen.add(selector);
let value = '';
if (isFormEl && inputType !== 'password') {
if (tag === 'select') {
const opt = el.options && el.options[el.selectedIndex];
value = opt ? opt.value : '';
} else {
value = (el.value || '').trim();
}
if (value.length > 40) value = value.substring(0, 37) + '...';
}
results.push({
tag,
role: el.getAttribute('role') || null,
text,
placeholder: placeholder || null,
input_type: tag === 'input' ? (inputType || 'text') : (tag === 'select' ? 'select' : null),
selector,
checked: !!el.checked,
value,
x: Math.round(rect.x),
y: Math.round(rect.y),
width: Math.round(rect.width),
height: Math.round(rect.height),
});
}
collect(document);
return JSON.stringify(results);
})()