From ccce26c3869091d91c99fc3b883d5e3c4b047ce7 Mon Sep 17 00:00:00 2001 From: Vula Builder Date: Wed, 3 Jun 2026 16:38:49 +0000 Subject: [PATCH] Deploy --- public/vula-editor.js | 72 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 public/vula-editor.js diff --git a/public/vula-editor.js b/public/vula-editor.js new file mode 100644 index 0000000..f266dd4 --- /dev/null +++ b/public/vula-editor.js @@ -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();} +}()); \ No newline at end of file