a03675113a
- .claude/agents/fn-orquestador/SKILL.md - .claude/commands/fn_claude.md - .claude/rules/INDEX.md - .claude/rules/cpp_apps.md - .claude/rules/ids_naming.md - CHANGELOG.md - apps/dag_engine/README.md - apps/dag_engine/api.go - apps/dag_engine/dags_migrated/example.yaml - apps/dag_engine/dags_migrated/example_lineage_tracking.yaml - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
121 lines
4.6 KiB
JavaScript
121 lines
4.6 KiB
JavaScript
// cdp_pick_element_js - JS injected via CDP Runtime.evaluate.
|
|
// Activates click-to-pick mode: hover highlights, click captures element selectors
|
|
// and prints via console.log so caller (navegator_dashboard) reads via
|
|
// Runtime.consoleAPICalled event.
|
|
//
|
|
// Inject as expression: see cdp_pick_element_js.md for the wrapping payload.
|
|
// Idempotent: re-injecting cleans up previous overlays before reattaching.
|
|
(function () {
|
|
if (window.__fn_pick_active) {
|
|
window.__fn_pick_cleanup && window.__fn_pick_cleanup();
|
|
}
|
|
window.__fn_pick_active = true;
|
|
|
|
const overlay = document.createElement('div');
|
|
overlay.id = '__fn_pick_overlay';
|
|
Object.assign(overlay.style, {
|
|
position: 'fixed', pointerEvents: 'none', zIndex: '2147483647',
|
|
border: '2px solid #f44', background: 'rgba(255,80,80,0.10)',
|
|
boxSizing: 'border-box', transition: 'all 30ms linear',
|
|
left: '0px', top: '0px', width: '0px', height: '0px',
|
|
});
|
|
document.body.appendChild(overlay);
|
|
|
|
function cssPath(el) {
|
|
if (!(el instanceof Element)) return '';
|
|
const path = [];
|
|
while (el && el.nodeType === Node.ELEMENT_NODE && el !== document.body) {
|
|
let seg = el.tagName.toLowerCase();
|
|
if (el.id) { seg += '#' + CSS.escape(el.id); path.unshift(seg); break; }
|
|
// Prefer class-based path when class is short and not utility-noise.
|
|
if (el.className && typeof el.className === 'string') {
|
|
const cls = el.className.trim().split(/\s+/).filter(c => c && c.length < 24 && !/^(active|hover|focus|selected|open|disabled|[0-9])/.test(c));
|
|
if (cls.length) seg += '.' + cls.map(CSS.escape).join('.');
|
|
}
|
|
// Disambiguate by nth-of-type when siblings share tag.
|
|
if (el.parentNode) {
|
|
const siblings = [...el.parentNode.children].filter(c => c.tagName === el.tagName);
|
|
if (siblings.length > 1) {
|
|
const idx = siblings.indexOf(el) + 1;
|
|
seg += `:nth-of-type(${idx})`;
|
|
}
|
|
}
|
|
path.unshift(seg);
|
|
el = el.parentNode;
|
|
if (path.length > 6) break;
|
|
}
|
|
return path.join(' > ');
|
|
}
|
|
|
|
function xpath(el) {
|
|
if (!el) return '';
|
|
if (el.id) return `//*[@id="${el.id}"]`;
|
|
const parts = [];
|
|
while (el && el.nodeType === Node.ELEMENT_NODE) {
|
|
let ix = 0;
|
|
for (const sib of el.parentNode ? el.parentNode.childNodes : []) {
|
|
if (sib === el) { parts.unshift(`${el.tagName.toLowerCase()}[${ix + 1}]`); break; }
|
|
if (sib.nodeType === 1 && sib.tagName === el.tagName) ix++;
|
|
}
|
|
el = el.parentNode;
|
|
if (el === document.body) { parts.unshift('body'); break; }
|
|
}
|
|
return '/html/body/' + parts.join('/');
|
|
}
|
|
|
|
function describe(el) {
|
|
if (!el) return null;
|
|
const r = el.getBoundingClientRect();
|
|
const attrs = {};
|
|
for (const a of el.attributes || []) attrs[a.name] = a.value;
|
|
return {
|
|
selector: cssPath(el),
|
|
xpath: xpath(el),
|
|
tag: el.tagName.toLowerCase(),
|
|
text: (el.innerText || el.textContent || '').trim().slice(0, 200),
|
|
attrs,
|
|
rect: { x: Math.round(r.left), y: Math.round(r.top), w: Math.round(r.width), h: Math.round(r.height) },
|
|
};
|
|
}
|
|
|
|
function onMove(e) {
|
|
const el = e.target;
|
|
if (!el || el === overlay) return;
|
|
const r = el.getBoundingClientRect();
|
|
overlay.style.left = r.left + 'px';
|
|
overlay.style.top = r.top + 'px';
|
|
overlay.style.width = r.width + 'px';
|
|
overlay.style.height = r.height + 'px';
|
|
}
|
|
|
|
function onClick(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const info = describe(e.target);
|
|
console.log('__fn_picked__', JSON.stringify(info));
|
|
cleanup();
|
|
}
|
|
|
|
function onKey(e) {
|
|
if (e.key === 'Escape') {
|
|
console.log('__fn_picked__', JSON.stringify({ cancelled: true }));
|
|
cleanup();
|
|
}
|
|
}
|
|
|
|
function cleanup() {
|
|
window.__fn_pick_active = false;
|
|
document.removeEventListener('mousemove', onMove, true);
|
|
document.removeEventListener('click', onClick, true);
|
|
document.removeEventListener('keydown', onKey, true);
|
|
overlay.remove();
|
|
}
|
|
window.__fn_pick_cleanup = cleanup;
|
|
|
|
document.addEventListener('mousemove', onMove, true);
|
|
document.addEventListener('click', onClick, true);
|
|
document.addEventListener('keydown', onKey, true);
|
|
|
|
return 'pick mode active';
|
|
})();
|