`;
break;
case 'clock':
html += ``;
break;
case 'date':
html += ``;
break;
case 'image':
html += ``;
break;
case 'video':
html += ``;
break;
case 'shape':
html += ``;
break;
case 'weather':
html += `
⛅ Loading...
`;
break;
case 'ticker':
html += `
Loading news...
`;
break;
case 'qr':
html += `
QR CODE
${el.data?.slice(0, 25)}
`;
break;
case 'countdown':
html += `
${el.label || ''}
`;
break;
case 'webpage':
html += ``;
break;
}
});
// Add ticker animation CSS
html += ``;
preview.innerHTML = html;
// Update dynamic elements
updateDynamic();
// Update properties panel
updateProps();
updateLayers();
}
function updateDynamic() {
elements.forEach((el, i) => {
if (el.type === 'clock') {
const clockEl = document.getElementById(`clock_${i}`);
if (clockEl) {
const update = () => {
const opts = { hour: '2-digit', minute: '2-digit' };
if (el.showSeconds) opts.second = '2-digit';
opts.hour12 = el.format !== '24h';
clockEl.textContent = new Date().toLocaleTimeString('en-US', opts);
};
update();
// Only set interval if element still exists
const iv = setInterval(() => { if (document.getElementById(`clock_${i}`)) update(); else clearInterval(iv); }, 1000);
}
}
if (el.type === 'date') {
const dateEl = document.getElementById(`date_${i}`);
if (dateEl) dateEl.textContent = new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
}
if (el.type === 'countdown') {
const cdEl = document.getElementById(`countdown_${i}`);
if (cdEl && el.targetDate) {
const update = () => {
const diff = new Date(el.targetDate) - new Date();
if (diff <= 0) { cdEl.textContent = 'NOW!'; return; }
const days = Math.floor(diff / 86400000);
const hours = Math.floor((diff % 86400000) / 3600000);
const mins = Math.floor((diff % 3600000) / 60000);
cdEl.textContent = `${days}d ${hours}h ${mins}m`;
};
update();
const iv = setInterval(() => { if (document.getElementById(`countdown_${i}`)) update(); else clearInterval(iv); }, 60000);
}
}
if (el.type === 'weather') {
const wEl = document.getElementById(`weather_${i}`);
if (wEl && el.location) {
fetch(`https://wttr.in/${encodeURIComponent(el.location)}?format=j1`).then(r => r.json()).then(d => {
const cur = d.current_condition?.[0];
if (cur) {
const temp = el.units === 'metric' ? cur.temp_C + '°C' : cur.temp_F + '°F';
wEl.textContent = `${temp} ${cur.weatherDesc?.[0]?.value || ''}`;
}
}).catch(() => { wEl.textContent = '⛅ ' + el.location; });
}
}
if (el.type === 'ticker') {
const tEl = document.getElementById(`ticker_${i}`);
if (tEl && el.feedUrl) {
fetch(`https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(el.feedUrl)}`).then(r => r.json()).then(d => {
tEl.textContent = (d.items || []).map(item => item.title).join(' • ') || 'No items';
}).catch(() => { tEl.textContent = 'Feed unavailable'; });
}
}
});
}
function updateProps() {
const panel = document.getElementById('propPanel');
const fields = document.getElementById('propFields');
if (selectedIdx < 0 || !elements[selectedIdx]) { panel.style.display = 'none'; return; }
panel.style.display = 'block';
const el = elements[selectedIdx];
let html = '';
// Common position
html += `
`;
if (el.type === 'text') {
html += `
${el.fontSize}px
`;
} else if (el.type === 'clock') {
html += `
`;
} else if (el.type === 'image' || el.type === 'video' || el.type === 'webpage') {
html += `
`;
if (el.type === 'video') html += `
`;
} else if (el.type === 'shape') {
html += `
';
list.querySelectorAll('[data-layer]').forEach(el => {
el.onclick = () => { selectedIdx = parseInt(el.dataset.layer); redraw(); };
});
}
function generateInnerHTML() {
let html = '';
elements.forEach((el, i) => {
switch (el.type) {
case 'text':
html += `
${el.text}
`;
break;
case 'clock':
html += `
`;
break;
case 'date':
html += `
`;
break;
case 'image':
html += ``;
break;
case 'video':
html += ``;
break;
case 'shape':
html += ``;
break;
case 'weather':
html += `
Loading...
`;
break;
case 'ticker':
html += `
Loading...
`;
break;
case 'countdown':
html += `
${el.label}
`;
break;
case 'webpage':
html += ``;
break;
}
});
return html;
}
function generateHTML() {
return `${generateInnerHTML()}`;
}
export function cleanup() {}