Arrastra aquí imágenes y videos sin límite
Soporta JPG, PNG, WEBP, MP4, WEBM, GIF. Almacenamiento en IndexedDB (cientos de MB / GB según navegador)
También puedes pegar URL externa:
Aún no hay archivos. ¡Sube tus fotos de cortinas!
':''); grid.querySelectorAll("[data-use]").forEach(b=>{ b.onclick=async()=>{ const rec = await getMediaBlob(b.dataset.use); const url = URL.createObjectURL(rec.blob); if(onSelect){ onSelect(url, rec); wrap.remove(); } else { navigator.clipboard.writeText(url); alert("URL temporal creada (válida en esta sesión). Para uso permanente, exporta."); } }; }); grid.querySelectorAll("[data-del]").forEach(b=>{ b.onclick=()=>{ if(!confirm("¿Borrar?")) return; const tx = db.transaction(STORE,"readwrite"); tx.objectStore(STORE).delete(b.dataset.del); tx.oncomplete=refreshGrid; }; }); } async function handleFiles(files){ for(const f of files){ const id = "media-"+Date.now()+"-"+Math.random().toString(36).slice(2); await saveMediaBlob(id, f, {name:f.name, type:f.type}); } await refreshGrid(); } fileInput.onchange = ()=> handleFiles(fileInput.files); drop.ondragover = e=>{ e.preventDefault(); drop.classList.add("dragover"); }; drop.ondragleave = ()=> drop.classList.remove("dragover"); drop.ondrop = e=>{ e.preventDefault(); drop.classList.remove("dragover"); handleFiles(e.dataTransfer.files); }; wrap.querySelector("#dz-add-url").onclick=()=>{ const u = wrap.querySelector("#dz-url-input").value.trim(); if(!u) return; // fetch and store as blob if possible fetch(u).then(r=>r.blob()).then(async blob=>{ const id = "media-url-"+Date.now(); await saveMediaBlob(id, blob, {name:u.split("/").pop(), type:blob.type, url:u}); refreshGrid(); }).catch(()=>alert("No se pudo descargar. La URL queda referenciada igual. Úsala directo.")); }; refreshGrid(); } function openBannerManager(){ const wrap = document.createElement("div"); wrap.className="dz-modal-pro"; wrap.innerHTML=`Crea, duplica y edita banners sin límite. Aparecerán en la web automáticamente.
No hay banners
"; } wrap.querySelector("#dz-new-banner").onclick=()=>{ const title = prompt("Título banner:"); if(title===null) return; cms.banners.push({id:"bn-"+Date.now(), title, text: prompt("Texto:")||"", image: prompt("URL imagen (o deja vacío y luego usa biblioteca):")||"", link: prompt("Link destino:")||"#"}); persist(); render(); injectBannersToPage(); }; wrap.querySelector("#dz-dup-banner").onclick=()=>{ if(!cms.banners.length) return alert("No hay banner para duplicar"); const last = JSON.parse(JSON.stringify(cms.banners[cms.banners.length-1])); last.id="bn-"+Date.now(); last.title+=" - Copia"; cms.banners.push(last); persist(); render(); injectBannersToPage(); }; list.onclick = e=>{ const idx = e.target.dataset.edit; if(idx!==undefined){ const b = cms.banners[idx]; b.title = prompt("Título:", b.title)||b.title; b.text = prompt("Texto:", b.text)||b.text; b.image = prompt("Imagen URL:", b.image)||b.image; b.link = prompt("Link:", b.link)||b.link; persist(); render(); injectBannersToPage(); } const del = e.target.dataset.del; if(del!==undefined){ if(confirm("¿Borrar banner?")){ cms.banners.splice(del,1); persist(); render(); injectBannersToPage(); } } }; render(); } function injectBannersToPage(){ let container = document.getElementById("dz-dynamic-banners"); if(!container){ container = document.createElement("div"); container.id="dz-dynamic-banners"; container.style.cssText="max-width:1280px;margin:20px auto;padding:0 16px;display:grid;gap:16px"; document.body.insertBefore(container, document.body.firstChild.nextSibling); } container.innerHTML = (cms.banners||[]).map(b=>`${b.text||''}
Duplica cualquier página, sección, producto, foto o texto. Estructura infinita.
No hay páginas custom. Crea la primera.
"; } wrap.querySelector("#dz-new-page").onclick=()=>{ const title = prompt("Nombre de la nueva página / categoría:"); if(!title) return; const slug = title.toLowerCase().replace(/\s+/g,'-').replace(/[^a-z0-9-]/g,''); pages.push({id:"page-"+Date.now(), title, slug, blocks:[]}); persist(); render(); alert("Página creada: /"+slug+" - Ahora puedes duplicar contenido dentro de ella con el modo edición."); }; wrap.querySelector("#dz-dup-dom").onclick=()=>{ wrap.remove(); enableSectionDuplicator(); }; list.onclick=e=>{ const dup = e.target.dataset.dup; if(dup!==undefined){ const copy = JSON.parse(JSON.stringify(pages[dup])); copy.id="page-"+Date.now(); copy.title+=" - Copia"; copy.slug+="-copia"; pages.splice(parseInt(dup)+1,0,copy); persist(); render(); } const del = e.target.dataset.del; if(del!==undefined){ if(confirm("¿Borrar página?")){ pages.splice(del,1); persist(); render(); } } }; render(); } function enableSectionDuplicator(){ alert("Modo duplicación: pasa el mouse sobre cualquier sección/card/imagen del sitio y verás el botón Duplicar. Activa también el Modo edición en la barra inferior para editar textos al instante."); cms.editMode=true; persist(); makeEditable(); } function exportAll(){ const data = {cms, localStorageSnapshot: {...localStorage}}; const blob = new Blob([JSON.stringify(data,null,2)], {type:"application/json"}); const a = document.createElement("a"); a.href=URL.createObjectURL(blob); a.download="decorazione-backup-completo-"+Date.now()+".json"; a.click(); } function importAll(){ const input = document.createElement("input"); input.type="file"; input.accept=".json"; input.onchange=async()=>{ const file = input.files[0]; if(!file) return; const text = await file.text(); try{ const data = JSON.parse(text); if(data.cms){ localStorage.setItem(CMS_KEY, JSON.stringify(data.cms)); } alert("Importado correctamente. Recargando..."); location.reload(); }catch(e){ alert("Archivo inválido: "+e.message); } }; input.click(); } async function initPro(){ await openDB(); injectAdminStyles(); buildDock(); injectBannersToPage(); if(cms.editMode) { const bar = document.createElement("div"); bar.id="dz-edit-bar"; bar.innerHTML=`MODO EDICIÓN ACTIVOClick en cualquier texto para editar • Hover para duplicar/borrar`; document.body.appendChild(bar); makeEditable(); } console.log("Decorazione CMS PRO v2 listo - Media ilimitada con IndexedDB"); } if(document.readyState==="loading") document.addEventListener("DOMContentLoaded", initPro); else initPro(); })();