diff --git a/app.js b/app.js index 7e9d283..320ffb1 100644 --- a/app.js +++ b/app.js @@ -736,6 +736,25 @@ function initPosterLab() { }); }); + // Trending Google Search Chips (1-Click Auto Fill) + const trendChips = document.querySelectorAll('#trendingPosterChips .trend-chip'); + trendChips.forEach(chip => { + chip.addEventListener('click', () => { + trendChips.forEach(c => c.classList.remove('active')); + chip.classList.add('active'); + + const brand = chip.getAttribute('data-brand'); + const title = chip.getAttribute('data-title'); + const price = chip.getAttribute('data-price'); + const badge = chip.getAttribute('data-badge'); + + if (brand && brandWatermarkSelect) brandWatermarkSelect.value = brand; + if (title && productInput) productInput.value = title; + if (price && productPrice) productPrice.value = price; + if (badge && customBadgeInput) customBadgeInput.value = badge; + }); + }); + let attachedImageBitmap = null; // Photo Input change @@ -947,26 +966,56 @@ async function resolveProductUrl(input) { } function loadImage(src) { - return new Promise((resolve, reject) => { - let loadUrl = src; - if (src.startsWith('http://') || src.startsWith('https://')) { - loadUrl = 'https://images.weserv.nl/?url=' + encodeURIComponent(src) + '&output=jpg&q=95'; + return new Promise((resolve) => { + if (!src) { + const placeholder = createFallbackProductCanvas('Product Spotlight'); + return resolve(placeholder); + } + + if (src.startsWith('data:')) { + const img = new Image(); + img.onload = () => resolve(img); + img.onerror = () => resolve(createFallbackProductCanvas('Product')); + img.src = src; + return; } const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => resolve(img); img.onerror = () => { - const fallbackImg = new Image(); - fallbackImg.crossOrigin = 'anonymous'; - fallbackImg.onload = () => resolve(fallbackImg); - fallbackImg.onerror = () => reject(new Error('Could not load image')); - fallbackImg.src = src; + // Try proxy + const proxyImg = new Image(); + proxyImg.crossOrigin = 'anonymous'; + proxyImg.onload = () => resolve(proxyImg); + proxyImg.onerror = () => { + // Fallback to high quality generated canvas graphic + resolve(createFallbackProductCanvas('Premium Product')); + }; + proxyImg.src = 'https://images.weserv.nl/?url=' + encodeURIComponent(src) + '&output=jpg&q=95'; }; - img.src = loadUrl; + img.src = src; }); } +function createFallbackProductCanvas(label = 'Product') { + const c = document.createElement('canvas'); + c.width = 600; + c.height = 600; + const ctx = c.getContext('2d'); + const g = ctx.createLinearGradient(0, 0, 600, 600); + g.addColorStop(0, '#38BDF8'); + g.addColorStop(1, '#8B5CF6'); + ctx.fillStyle = g; + ctx.roundRect ? ctx.roundRect(80, 80, 440, 440, 40) : ctx.fillRect(80, 80, 440, 440); + ctx.fill(); + ctx.fillStyle = '#FFFFFF'; + ctx.font = 'bold 36px Plus Jakarta Sans, sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText('✨ ' + label, 300, 310); + return c; +} + // --- 3D Canvas Multi-Theme Rendering Engine --- async function render3DCommercialPoster(canvas, productImg, title, price, badgeText, theme = 'obsidian_gold', aspect = '1:1', brand = 'glamourstouch') { const ctx = canvas.getContext('2d'); @@ -1184,9 +1233,9 @@ async function render3DCommercialPoster(canvas, productImg, title, price, badgeT ctx.fillStyle = contactShadow; drawOval(ctx, stageCx, topTierY + 2, destW * 0.38, 12); - // Product Image (Multiply blended to wipe white background) + // Product Image (Crisp Solid Blend) ctx.save(); - ctx.globalCompositeOperation = 'multiply'; + ctx.globalCompositeOperation = 'source-over'; ctx.drawImage(productImg, destLeft, destTop, destW, destH); ctx.restore(); @@ -1218,8 +1267,7 @@ async function render3DCommercialPoster(canvas, productImg, title, price, badgeT ctx.font = '16px Plus Jakarta Sans, sans-serif'; ctx.fillText(brandInfo.headerSub, w / 2, 80); - // 7. Product Title Card - const cleanTitle = title.length > 60 ? title.substring(0, 58) + '...' : title; + // 7. Product Title Card (Smart Multi-line wrap) const titleCardTop = h - (aspect === '9:16' ? 320 : 235); const titleCardBottom = h - (aspect === '9:16' ? 150 : 128); @@ -1233,13 +1281,25 @@ async function render3DCommercialPoster(canvas, productImg, title, price, badgeT ctx.stroke(); ctx.fillStyle = '#FFFFFF'; - ctx.font = 'bold 27px Plus Jakarta Sans, Hind Siliguri, sans-serif'; + ctx.font = 'bold 26px Plus Jakarta Sans, Hind Siliguri, sans-serif'; ctx.textAlign = 'center'; - ctx.fillText(cleanTitle, w / 2, titleCardTop + 42); + + // Smart single/double line title drawing + const titleWords = title.split(' '); + if (titleWords.length > 5 && title.length > 35) { + const mid = Math.ceil(titleWords.length / 2); + const line1 = titleWords.slice(0, mid).join(' '); + const line2 = titleWords.slice(mid).join(' '); + ctx.fillText(line1, w / 2, titleCardTop + 34); + ctx.fillText(line2, w / 2, titleCardTop + 62); + } else { + const cleanTitle = title.length > 55 ? title.substring(0, 52) + '...' : title; + ctx.fillText(cleanTitle, w / 2, titleCardTop + 42); + } ctx.fillStyle = currentTheme.goldRim; ctx.font = 'bold 21px Hind Siliguri, sans-serif'; - ctx.fillText(`${price} • ১০০% আসল গ্যারান্টি • দ্রুত হোম ডেলিভারি`, w / 2, titleCardTop + 80); + ctx.fillText(`${price} • ১০০% আসল গ্যারান্টি • দ্রুত হোম ডেলিভারি`, w / 2, titleCardTop + 88); // 8. Bottom WhatsApp Banner const footerHeight = aspect === '9:16' ? 140 : 118; @@ -1260,7 +1320,7 @@ async function render3DCommercialPoster(canvas, productImg, title, price, badgeT ctx.fillText(`🌐 Website: ${brandInfo.footerSite} • 📞 Hotline: ${brandInfo.footerHotline}`, 35, footerTop + 90); // QR Code - await drawWhatsAppQrOnCanvas(ctx, w - 130, footerTop + 11, 96); + await drawWhatsAppQrOnCanvas(ctx, w - 130, footerTop + 11, 96, brandInfo.footerHotline); // Outer Border ctx.strokeStyle = currentTheme.goldRim + '99'; @@ -1316,29 +1376,48 @@ function drawPillBadge(ctx, x, y, text, isLeft, goldColor = '#E5B83A') { ctx.fillText(text, pillX + 14, pillY + 24); } -async function drawWhatsAppQrOnCanvas(ctx, x, y, size) { +async function drawWhatsAppQrOnCanvas(ctx, x, y, size, hotline = '01712-426871') { ctx.fillStyle = '#FFFFFF'; roundRect(ctx, x - 4, y - 4, size + 8, size + 8, 10); ctx.fill(); - const qrContainer = document.getElementById('qrcodeHidden'); - if (qrContainer) { - qrContainer.innerHTML = ''; - new QRCode(qrContainer, { - text: 'https://wa.me/8801712426871', - width: size, - height: size, - colorDark: '#0B2E13', - colorLight: '#FFFFFF', - correctLevel: QRCode.CorrectLevel.M - }); + const rawPhone = hotline.replace(/\D/g, ''); + const waUrl = `https://wa.me/88${rawPhone.startsWith('88') ? rawPhone.slice(2) : rawPhone.replace(/^0/, '')}`; - await new Promise(r => setTimeout(r, 80)); - const qrImg = qrContainer.querySelector('img') || qrContainer.querySelector('canvas'); - if (qrImg) { - ctx.drawImage(qrImg, x, y, size, size); + const qrContainer = document.getElementById('qrcodeHidden'); + if (qrContainer && typeof QRCode !== 'undefined') { + try { + qrContainer.innerHTML = ''; + new QRCode(qrContainer, { + text: waUrl, + width: size, + height: size, + colorDark: '#0B2E13', + colorLight: '#FFFFFF', + correctLevel: QRCode.CorrectLevel.M + }); + + await new Promise(r => setTimeout(r, 60)); + const qrImg = qrContainer.querySelector('img') || qrContainer.querySelector('canvas'); + if (qrImg) { + ctx.drawImage(qrImg, x, y, size, size); + return; + } + } catch (e) { + console.warn('QRCode render exception:', e); } } + + // Resilient High-Contrast Vector QR Fallback + ctx.fillStyle = '#0B2E13'; + ctx.fillRect(x + 4, y + 4, size - 8, size - 8); + ctx.fillStyle = '#FFFFFF'; + ctx.fillRect(x + 10, y + 10, size - 20, size - 20); + ctx.fillStyle = '#0B2E13'; + ctx.font = 'bold 13px Plus Jakarta Sans, sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText('SCAN', x + size / 2, y + size / 2 - 2); + ctx.fillText('ORDER', x + size / 2, y + size / 2 + 14); } // --- Customer Care & Inbox AI Dedicated Studio --- diff --git a/index.html b/index.html index 4b0cfbe..cf96321 100644 --- a/index.html +++ b/index.html @@ -97,6 +97,23 @@ + +