This commit is contained in:
Vula Builder
2026-06-04 09:16:58 +00:00
parent dd424cf443
commit f34ee6c4e8
+72
View File
@@ -0,0 +1,72 @@
(function(){
let enabled=false,overlay=null,badge=null;
function init(){
if(document.getElementById('__vula_ov'))return;
overlay=document.createElement('div');
overlay.id='__vula_ov';
overlay.style.cssText='position:fixed;pointer-events:none;border:2px solid #f97316;border-radius:3px;z-index:999999;transition:top .08s,left .08s,width .08s,height .08s;display:none;box-sizing:border-box;';
badge=document.createElement('div');
badge.id='__vula_badge';
badge.style.cssText='position:fixed;background:#f97316;color:#fff;font:bold 11px/1.4 monospace;padding:3px 8px;border-radius:3px;z-index:1000000;pointer-events:none;display:none;white-space:nowrap;';
document.body.appendChild(overlay);
document.body.appendChild(badge);
}
function cap(s){return s.charAt(0).toUpperCase()+s.slice(1);}
function classify(el){
let sec='',n=el;
while(n&&n!==document.body){
// data-vula-section injected by AICodeValidator post-pass — most reliable source
if(n.dataset&&n.dataset.vulaSection){sec=n.dataset.vulaSection;break;}
const cls=(n.className||'').toString(),tag=n.tagName.toLowerCase();
if(/hero|about|services|contact|team|pricing|faq|gallery|testimonial|footer|header|navigation/i.test(cls)){
sec=cls.split(' ').find(c=>/hero|about|services|contact|team|pricing|faq|gallery|testimonial|footer|header|navigation/i.test(c))||tag;break;
}
if(['section','nav','header','footer'].includes(tag)){sec=tag;break;}
n=n.parentElement;
}
const tag=el.tagName.toLowerCase();
let type='element',label='';
if(tag==='img'){type='image';label=el.alt||'Image';}
else if(tag==='button'||(tag==='a'&&el.href)){type='button';label=(el.textContent||'').trim().slice(0,30)||'Button';}
else if(/^h[1-4]$/.test(tag)){type='heading';label=(el.textContent||'').trim().slice(0,40)||'Heading';}
else if(tag==='p'||tag==='span'){type='text';label=(el.textContent||'').trim().slice(0,40)||'Text';}
else if(tag==='input'||tag==='textarea'){type='input';label=el.placeholder||el.name||'Input';}
else{type='section';label=sec||tag;}
return{section:sec,type,label,tag,className:(el.className||'').toString()};
}
function hl(el){
if(!overlay)return;
const r=el.getBoundingClientRect();
overlay.style.cssText=overlay.style.cssText;
overlay.style.display='block';
overlay.style.top=(r.top-2)+'px';overlay.style.left=(r.left-2)+'px';
overlay.style.width=(r.width+4)+'px';overlay.style.height=(r.height+4)+'px';
const info=classify(el);
badge.style.display='block';
badge.textContent=(info.section?cap(info.section)+' ':'')+info.label;
badge.style.top=Math.max(4,r.top-24)+'px';badge.style.left=r.left+'px';
}
function over(e){if(!enabled)return;e.stopPropagation();init();hl(e.target);}
function sendToParent(msg){
// Works in both iframe (window.parent) and new tab (window.opener)
var t=window.parent!==window?window.parent:(window.opener||null);
if(t)t.postMessage(msg,'*');
}
function click(e){
if(!enabled)return;e.preventDefault();e.stopPropagation();
var info=classify(e.target);
// Use elementType to avoid overwriting the message type field
sendToParent({type:'vula:selected',elementType:info.type,section:info.section,label:info.label,tag:info.tag,className:info.className});
}
function enable(){enabled=true;init();document.addEventListener('mouseover',over,true);document.addEventListener('click',click,true);document.body.style.cursor='crosshair';}
function disable(){enabled=false;document.removeEventListener('mouseover',over,true);document.removeEventListener('click',click,true);document.body.style.cursor='';if(overlay){overlay.style.display='none';badge.style.display='none';}}
window.addEventListener('message',function(e){
if(!e.data)return;
if(e.data.type==='vula:enable')enable();
else if(e.data.type==='vula:disable')disable();
});
// Announce ready so IDE can re-send enable state after iframe reload
function announceReady(){sendToParent({type:'vula:ready'});}
if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',announceReady);}
else{announceReady();}
}());