diff --git a/app.js b/app.js
index f3c9ea7..e902208 100644
--- a/app.js
+++ b/app.js
@@ -1,60 +1,79 @@
-// Google Opal AI - iOS Studio & 3D Poster Lab Engine
+// Google Opal AI - Master Mini-App Studio & 3D Poster Lab Engine v5.0
document.addEventListener('DOMContentLoaded', () => {
initNavigation();
initSettings();
initPosterLab();
+ initMiniAppsHub();
+ initMiniAppBuilder();
initInboxAI();
initHistory();
});
-// --- 1. Tab Navigation ---
-function initNavigation() {
+// --- Global Navigation ---
+window.switchToTab = function(tabId) {
const navItems = document.querySelectorAll('.nav-item');
const tabPages = document.querySelectorAll('.tab-page');
+ navItems.forEach(n => {
+ if (n.getAttribute('data-tab') === tabId) {
+ n.classList.add('active');
+ } else {
+ n.classList.remove('active');
+ }
+ });
+
+ tabPages.forEach(p => {
+ if (p.id === tabId) {
+ p.classList.add('active');
+ } else {
+ p.classList.remove('active');
+ }
+ });
+
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+};
+
+function initNavigation() {
+ const navItems = document.querySelectorAll('.nav-item');
navItems.forEach(item => {
item.addEventListener('click', () => {
const targetTabId = item.getAttribute('data-tab');
-
- navItems.forEach(n => n.classList.remove('active'));
- tabPages.forEach(p => p.classList.remove('active'));
-
- item.classList.add('active');
- const targetPage = document.getElementById(targetTabId);
- if (targetPage) targetPage.classList.add('active');
- window.scrollTo({ top: 0, behavior: 'smooth' });
+ switchToTab(targetTabId);
});
});
// Language Toggle
const langBtn = document.getElementById('langToggleBtn');
let currentLang = 'BN';
- langBtn.addEventListener('click', () => {
- currentLang = currentLang === 'BN' ? 'EN' : 'BN';
- langBtn.textContent = currentLang === 'BN' ? 'đ āĻŦāĻžāĻ' : 'đ EN';
- });
-}
-
-// Global App Switcher
-window.openAppRunner = function(appType) {
- if (appType === 'inbox_ai') {
- document.querySelector('.nav-item[data-tab="tabInboxAI"]').click();
- } else {
- document.querySelector('.nav-item[data-tab="tabPosterLab"]').click();
+ if (langBtn) {
+ langBtn.addEventListener('click', () => {
+ currentLang = currentLang === 'BN' ? 'EN' : 'BN';
+ langBtn.textContent = currentLang === 'BN' ? 'đ āĻŦāĻžāĻ' : 'đ EN';
+ });
}
-};
+}
window.copyText = function(elementId) {
const el = document.getElementById(elementId);
- if (el && el.textContent) {
- navigator.clipboard.writeText(el.textContent).then(() => {
+ if (el) {
+ const text = el.value || el.textContent || '';
+ navigator.clipboard.writeText(text).then(() => {
+ alert('â
Copied to Clipboard!');
+ }).catch(() => {
+ // Fallback
+ const ta = document.createElement('textarea');
+ ta.value = text;
+ document.body.appendChild(ta);
+ ta.select();
+ document.execCommand('copy');
+ document.body.removeChild(ta);
alert('â
Copied to Clipboard!');
});
}
};
-// --- 2. Settings & Gemini/Gemma API ---
+// --- Settings & Gemini 2.0 API Engine ---
function initSettings() {
const apiKeyInput = document.getElementById('apiKeyInput');
const modelSelect = document.getElementById('modelSelect');
@@ -64,47 +83,54 @@ function initSettings() {
// Load saved settings
const savedKey = localStorage.getItem('opal_gemini_api_key') || '';
- const savedModel = localStorage.getItem('opal_selected_model') || 'gemini-3.6-flash';
+ const savedModel = localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash';
const savedN8n = localStorage.getItem('opal_n8n_webhook') || 'https://n8n.auraajenticai.cloud/webhook/gt-auto-post';
- apiKeyInput.value = savedKey;
- modelSelect.value = savedModel;
+
+ if (apiKeyInput) apiKeyInput.value = savedKey;
+ if (modelSelect) modelSelect.value = savedModel;
if (n8nWebhookInput) n8nWebhookInput.value = savedN8n;
- saveBtn.addEventListener('click', async () => {
- const key = apiKeyInput.value.trim();
- const model = modelSelect.value;
- const n8nUrl = n8nWebhookInput ? n8nWebhookInput.value.trim() : '';
+ if (saveBtn) {
+ saveBtn.addEventListener('click', async () => {
+ const key = apiKeyInput.value.trim();
+ const model = modelSelect.value;
+ const n8nUrl = n8nWebhookInput ? n8nWebhookInput.value.trim() : '';
- if (n8nUrl) {
- localStorage.setItem('opal_n8n_webhook', n8nUrl);
- }
-
- if (!key) {
- alert('Settings saved!');
- return;
- }
-
- statusMsg.classList.remove('hidden');
- statusMsg.style.color = '#F472B6';
- statusMsg.textContent = 'âŗ Testing AI connection with Google...';
-
- try {
- const res = await callGeminiApi(key, model, "Respond with: 'Opal AI Connected successfully!'");
- if (res) {
- localStorage.setItem('opal_gemini_api_key', key);
- localStorage.setItem('opal_selected_model', model);
- statusMsg.style.color = '#10B981';
- statusMsg.textContent = `â
Settings Saved & Verified: ${res.trim()}`;
+ if (n8nUrl) {
+ localStorage.setItem('opal_n8n_webhook', n8nUrl);
}
- } catch (e) {
- statusMsg.style.color = '#EF4444';
- statusMsg.textContent = `â Error: ${e.message}`;
- }
- });
+
+ if (!key) {
+ localStorage.setItem('opal_selected_model', model);
+ statusMsg.classList.remove('hidden');
+ statusMsg.style.color = '#10B981';
+ statusMsg.textContent = 'â
Settings saved (Offline templates active).';
+ return;
+ }
+
+ statusMsg.classList.remove('hidden');
+ statusMsg.style.color = '#F472B6';
+ statusMsg.textContent = 'âŗ Testing Gemini 2.0 AI connection with Google...';
+
+ try {
+ const res = await callGeminiApi(key, model, "Respond with: 'Google Opal AI Connected successfully!'");
+ if (res) {
+ localStorage.setItem('opal_gemini_api_key', key);
+ localStorage.setItem('opal_selected_model', model);
+ statusMsg.style.color = '#10B981';
+ statusMsg.textContent = `â
Verified & Active: ${res.trim()}`;
+ }
+ } catch (e) {
+ statusMsg.style.color = '#EF4444';
+ statusMsg.textContent = `â Connection Error: ${e.message}`;
+ }
+ });
+ }
}
async function callGeminiApi(apiKey, model, prompt) {
- const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
+ const activeModel = model || localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash';
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${activeModel}:generateContent?key=${apiKey}`;
const payload = {
contents: [{ parts: [{ text: prompt }] }],
generationConfig: { temperature: 0.7, maxOutputTokens: 2048 }
@@ -117,7 +143,7 @@ async function callGeminiApi(apiKey, model, prompt) {
});
if (!response.ok) {
- const errData = await response.json();
+ const errData = await response.json().catch(() => ({}));
throw new Error(errData?.error?.message || `HTTP ${response.status}`);
}
@@ -125,10 +151,463 @@ async function callGeminiApi(apiKey, model, prompt) {
return data?.candidates?.[0]?.content?.parts?.[0]?.text || '';
}
-// --- 3. 3D Poster Lab Engine (True 3D Dual-Tier Gold Podium) ---
+// --- Curated Pre-Built Mini-Apps Suite ---
+const PREBUILT_MINI_APPS = [
+ {
+ id: 'gt_studio',
+ name: '3D Commercial Poster Lab',
+ emoji: 'đ¨',
+ category: 'ecommerce',
+ badge: 'GEM',
+ desc: 'āĻĒā§āϰāĻĄāĻžāĻā§āĻā§āϰ āĻāĻŦāĻŋ, ā§ŠāĻĄāĻŋ āĻŽāĻžāϰā§āĻŦā§āϞ āϏā§āĻā§āĻ āĻ WhatsApp QR āϏāĻš 4K āĻĢā§āϏāĻŦā§āĻ āĻŦāĻŋāĻā§āĻāĻžāĻĒāύ āĻĒā§āϏā§āĻāĻžāϰ āϤā§āϰāĻŋ āĻāϰā§āĨ¤',
+ steps: '7 Steps âĸ 3D Canvas',
+ isPosterLab: true
+ },
+ {
+ id: 'inbox_ai',
+ name: 'Customer Care & Sales Auto-Reply',
+ emoji: 'đŦ',
+ category: 'sales',
+ badge: 'AI',
+ desc: 'āĻĢā§āϏāĻŦā§āĻ āĻ āĻšā§ā§āĻžāĻāϏāĻ
ā§āϝāĻžāĻĒā§āϰ āĻāĻžāϏā§āĻāĻŽāĻžāϰā§āϰ āĻĒā§āϰāĻļā§āύā§āϰ ā§ŠāĻāĻŋ āĻāύāϏā§āĻā§āϝāĻžāύā§āĻ āĻŽāĻŋāώā§āĻāĻŋ āĻ āϏā§āϞāϏ āĻā§āϞā§āĻāĻŋāĻ āĻāϤā§āϤāϰ āĻŦāĻžāύāĻžā§āĨ¤',
+ steps: '3 Steps âĸ Sales Closer',
+ fields: [
+ { id: 'field1', label: 'āĻāĻžāϏā§āĻāĻŽāĻžāϰ āĻŽā§āϏā§āĻ (Customer Message)', placeholder: 'e.g. āĻāĻĒāύāĻžāĻĻā§āϰ āϏāĻŋāϰāĻžāĻŽā§āϰ āĻĻāĻžāĻŽ āĻāϤ āĻāϰ āĻĄā§āϞāĻŋāĻāĻžāϰāĻŋ āĻāĻžāϰā§āĻ āĻāϤ?' },
+ { id: 'field2', label: 'āĻĻā§āĻāĻžāύā§āϰ āĻ
āĻĢāĻžāϰ āĻŦāĻž āĻšāĻāϞāĻžāĻāύ (Store Context)', placeholder: 'e.g. āĻĻāĻžāĻŽ ā§§ā§Ļā§Ŧā§Ļ āĻāĻžāĻāĻž, āĻĸāĻžāĻāĻž ā§ā§Ļ āĻāĻžāĻāĻž, āĻšāĻāϞāĻžāĻāύ ā§Ļā§§ā§ā§§ā§¨-ā§Ē⧍ā§Ŧā§Žā§ā§§' }
+ ],
+ promptTemplate: `You are an expert Bangladeshi e-commerce sales closer.
+Customer message: {{field1}}
+Store context: {{field2}}
+
+Write 3 distinct ready-to-copy Bengali replies:
+1. Instant sweet greeting with price & benefits
+2. Direct order closing script asking for Name, Address, Phone
+3. 1-Click WhatsApp direct order message with link https://wa.me/8801712426871`
+ },
+ {
+ id: 'veo_prompter',
+ name: 'Google Veo 3.1 Video Ad Prompter',
+ emoji: 'đŦ',
+ category: 'social',
+ badge: 'VEO 3.1',
+ desc: 'āĻā§āĻāϞ Veo ā§Š.ā§§ āĻ Sora-āϰ āĻāύā§āϝ āϏāĻŋāύā§āĻŽāĻžāĻāĻŋāĻ 9:16 āĻāĻŋāĻĄāĻŋāĻ āĻĒā§āϰāĻŽā§āĻĒāĻ, āĻā§āϝāĻžāĻŽā§āϰāĻž āĻŽā§āĻļāύ āĻ āϞāĻžāĻāĻāĻŋāĻ āĻāĻžāĻāĻĄ āĻŦāĻžāύāĻžā§āĨ¤',
+ steps: '4 Steps âĸ AI Video',
+ fields: [
+ { id: 'field1', label: 'āĻĒā§āϰāĻĄāĻžāĻā§āĻā§āϰ āύāĻžāĻŽ āĻ āϧāϰāĻŖ (Product)', placeholder: 'e.g. Medicube Collagen Jelly Cream' },
+ { id: 'field2', label: 'āĻāĻŋāĻĄāĻŋāĻāϰ āĻāĻžāĻāĻŦ āĻ āϏā§āĻāĻžāĻāϞ (Style/Vibe)', placeholder: 'e.g. Luxury Korean Aesthetic Glow, Neon reflections, 4k macro' }
+ ],
+ promptTemplate: `You are a world-class AI Video Prompt Engineer for Google Veo 3.1 and Sora.
+Create an ultra-detailed, cinematic 9:16 vertical video prompt for:
+Product: {{field1}}
+Style/Vibe: {{field2}}
+
+Include:
+1. Master Cinematic Prompt (in English for AI video model)
+2. Camera Movements (Macro close-up, 360 rotation, slow motion drop)
+3. Studio Lighting & Physics (Softbox, caustics, rim light)
+4. Bengali Narration / Voiceover Script (15 seconds)`
+ },
+ {
+ id: 'risk_scorer',
+ name: 'Gemini COD Fraud & Return Risk Shield',
+ emoji: 'đĄī¸',
+ category: 'tools',
+ badge: 'AI SHIELD',
+ desc: 'āĻāĻžāϏā§āĻāĻŽāĻžāϰā§āϰ āĻĢā§āύ āύāĻžāĻŽā§āĻŦāĻžāϰ, āĻ āĻŋāĻāĻžāύāĻž āĻ āĻ
āϰā§āĻĄāĻžāϰā§āϰ āĻĒāϰāĻŋāĻŽāĻžāĻŖ āĻŦāĻŋāĻļā§āϞā§āώāĻŖ āĻāϰ⧠āϰāĻŋāĻāĻžāϰā§āύ āĻĢā§āϰāĻĄ āϰāĻŋāϏā§āĻ āϏā§āĻā§āϰ āĻĻā§ā§āĨ¤',
+ steps: 'Real-time âĸ Gemini 2.0',
+ fields: [
+ { id: 'field1', label: 'āĻāĻžāϏā§āĻāĻŽāĻžāϰā§āϰ āĻĢā§āύ āύāĻžāĻŽā§āĻŦāĻžāϰ (Phone Number)', placeholder: 'e.g. 01819998877' },
+ { id: 'field2', label: 'āĻ āĻŋāĻāĻžāύāĻž āĻ āĻ
āϰā§āĻĄāĻžāϰā§āϰ āĻŽā§āϞā§āϝ (Address & Amount)', placeholder: 'e.g. Mirpur 10, Dhaka - Amount: ā§ŗ 3,200' }
+ ],
+ promptTemplate: `You are an advanced e-commerce Fraud Risk Analysis Engine for Bangladesh logistics.
+Phone: {{field1}}
+Order Info: {{field2}}
+
+Evaluate:
+1. Risk Level: (LOW / MEDIUM / HIGH)
+2. Recommendation: (Proceed with COD / Take Advance Delivery Charge ā§ŗ150 / Call to verify)
+3. Short reasoning in clear Bengali.`
+ },
+ {
+ id: 'ecommerce_launch',
+ name: 'E-Commerce Viral Product Launch Kit',
+ emoji: 'đī¸',
+ category: 'ecommerce',
+ badge: 'VIRAL',
+ desc: 'āϝā§āĻā§āύ⧠āĻĒāĻŖā§āϝā§āϰ āĻāĻāϰā§āώāĻŖā§ā§ āĻĢā§āϏāĻŦā§āĻ āĻĒā§āϏā§āĻ āĻā§āϝāĻžāĻĒāĻļāύ, āĻĒā§āĻāύ āĻĒā§ā§āύā§āĻ āĻšā§āĻ āĻ āĻ
āĻĢāĻžāϰ āĻāĻĒāĻŋ āϤā§āϰāĻŋ āĻāϰā§āĨ¤',
+ steps: '5 Steps âĸ Marketing',
+ fields: [
+ { id: 'field1', label: 'āĻĒā§āϰāĻĄāĻžāĻā§āĻā§āϰ āύāĻžāĻŽ āĻ āĻā§āϝāĻžāĻāĻžāĻāϰāĻŋ (Product Name)', placeholder: 'e.g. Korean Snail Mucin Power Essence' },
+ { id: 'field2', label: 'āĻŽā§āϞ āϏā§āĻŦāĻŋāϧāĻž āĻ āĻ
āĻĢāĻžāϰ (Key Benefits & Price)', placeholder: 'e.g. āĻŦā§āϰāĻŖā§āϰ āĻĻāĻžāĻ āĻĻā§āϰ āĻāϰā§, āĻĄāĻŋāĻĒ āĻŽā§ā§āĻļā§āĻāĻžāϰāĻžāĻāĻāĻŋāĻ, āĻĻāĻžāĻŽ ā§ŗā§§,ā§Ēā§Ģā§Ļ (āĻ
āĻĢāĻžāϰ ⧍ā§Ģ% āĻāĻžā§)' }
+ ],
+ promptTemplate: `You are a high-converting Facebook ad copywriter in Bangladesh.
+Create a complete viral product launch post in attractive Bengali for:
+Product: {{field1}}
+Benefits & Offer: {{field2}}
+
+Format:
+- 3 Viral Opening Hooks (Emoji heavy)
+- Problem & Solution story
+- Special Limited Time Offer & Delivery Info
+- Urgent Call-to-Action with WhatsApp & Website link`
+ },
+ {
+ id: 'tiktok_hooks',
+ name: 'TikTok & Meta Reels Viral Hook Matrix',
+ emoji: 'đĨ',
+ category: 'social',
+ badge: 'HOOKS',
+ desc: 'āĻĒā§āϰāĻĨāĻŽ ā§Š āϏā§āĻā§āύā§āĻĄā§ āĻāĻžāϏā§āĻāĻŽāĻžāϰā§āϰ āĻā§āĻ āĻāĻāĻā§ āĻĢā§āϞāĻžāϰ āĻŽāϤ⧠ā§ĢāĻāĻŋ āϏāĻžāĻāĻā§āϞāĻāĻŋāĻā§āϝāĻžāϞ āĻāĻžāĻāϰāĻžāϞ āĻšā§āĻ āϤā§āϰāĻŋ āĻāϰā§āĨ¤',
+ steps: '5 Viral Hooks âĸ Reels',
+ fields: [
+ { id: 'field1', label: 'āĻĒā§āϰāĻĄāĻžāĻā§āĻ āĻŦāĻž āĻŦāĻŋāώā§ā§āϰ āύāĻžāĻŽ (Product / Topic)', placeholder: 'e.g. Korean Glass Skin Facial Foam' },
+ { id: 'field2', label: 'āĻāĻžāϰā§āĻā§āĻ āĻ
āĻĄāĻŋā§ā§āύā§āϏ (Target Audience)', placeholder: 'e.g. āϝāĻžāĻĻā§āϰ āĻŽā§āĻā§ āϰā§āĻĻā§ āĻĒā§ā§āĻž āĻĻāĻžāĻ āĻ āĻŦā§āϰāĻŖā§āϰ āϏāĻŽāϏā§āϝāĻž āϰā§ā§āĻā§' }
+ ],
+ promptTemplate: `You are a viral TikTok & Instagram Reels growth strategist.
+Create 5 powerful 3-second visual + verbal hooks in Bengali for:
+Product: {{field1}}
+Target Audience: {{field2}}
+
+Format each hook with:
+- Visual Action (Screen text & actor action)
+- Spoken Audio Hook (High curiosity / urgency)
+- Psychology trigger explanation`
+ },
+ {
+ id: 'urgency_sales',
+ name: 'Flash Sale & Urgency Campaign Copywriter',
+ emoji: 'đˇī¸',
+ category: 'sales',
+ badge: 'OFFER',
+ desc: '⧍ā§Ē āĻāĻŖā§āĻāĻžāϰ āĻĢā§āϞā§āϝāĻžāĻļ āϏā§āϞ, āϏā§āĻāĻ āĻā§āϞāĻŋā§āĻžāϰā§āύā§āϏ āĻ āĻāĻĻ/āĻŦā§āĻļāĻžāĻā§ āĻ
āĻĢāĻžāϰā§āϰ āĻšāĻžāĻ-āĻāύāĻāĻžāϰā§āĻāĻŋāĻ āĻāĻĒāĻŋ āĻŦāĻžāύāĻžā§āĨ¤',
+ steps: 'Flash Sales âĸ Copy',
+ fields: [
+ { id: 'field1', label: 'āĻ
āĻĢāĻžāϰā§āϰ āύāĻžāĻŽ āĻ āĻĄāĻŋāϏāĻāĻžāĻāύā§āĻ (Offer Details)', placeholder: 'e.g. 48 Hours Weekend Flash Sale - Flat 35% OFF' },
+ { id: 'field2', label: 'āĻļāϰā§āϤ āĻ āĻĢā§āϰāĻŋ āĻĄā§āϞāĻŋāĻāĻžāϰāĻŋ (Conditions & Perks)', placeholder: 'e.g. āĻĒā§āϰāĻĨāĻŽ ā§Ģā§Ļ āĻāύā§āϰ āĻāύā§āϝ āĻĢā§āϰāĻŋ āĻāĻŋāĻĢāĻ āĻ āĻĢā§āϰāĻŋ āĻšā§āĻŽ āĻĄā§āϞāĻŋāĻāĻžāϰāĻŋ' }
+ ],
+ promptTemplate: `Create an irresistible, high-urgency Flash Sale ad copy in natural Bengali for:
+Campaign: {{field1}}
+Perks: {{field2}}
+
+Include FOMO triggers, countdown psychological anchors, and clear WhatsApp booking steps.`
+ },
+ {
+ id: 'courier_formatter',
+ name: 'Steadfast & Pathao Consignment Formatter',
+ emoji: 'đĻ',
+ category: 'tools',
+ badge: 'LOGISTICS',
+ desc: 'āĻ
āϰā§āĻĄāĻžāϰā§āϰ āĻā§āĻā§āϏāĻ āĻĨā§āĻā§ ā§§-āĻā§āϞāĻŋāĻā§ āĻā§āϰāĻŋā§āĻžāϰ āĻāύā§āĻā§āϰāĻŋ āĻĢāϰāĻŽā§āϝāĻžāĻ āĻ āĻĒā§āϰāĻŋāύā§āĻ āϞā§āĻŦā§āϞ āϤā§āϰāĻŋ āĻāϰā§āĨ¤',
+ steps: '1-Click âĸ Logistics',
+ fields: [
+ { id: 'field1', label: 'āĻāĻžāϏā§āĻāĻŽāĻžāϰā§āϰ āϏāĻŽā§āĻĒā§āϰā§āĻŖ āĻ
āϰā§āĻĄāĻžāϰ āĻā§āĻā§āϏāĻ (Order Text)', placeholder: 'e.g. āύāĻžāĻŽ: āϤāĻžāύāĻā§āϰ āĻšāĻžāϏāĻžāύ, āĻĢā§āύ: 01712345678, āĻ āĻŋāĻāĻžāύāĻž: āĻŦāĻžā§āĻŋ ⧧⧍, āϰā§āĻĄ ā§Ģ, āϧāĻžāύāĻŽāύā§āĻĄāĻŋ, āĻĸāĻžāĻāĻžāĨ¤ āĻĒā§āϰāĻĄāĻžāĻā§āĻ: ā§§ āĻĒāĻŋāϏ āϏāĻŋāϰāĻžāĻŽ (ā§ŗā§§ā§Ļā§Ŧā§Ļ)' },
+ { id: 'field2', label: 'āĻĄā§āϞāĻŋāĻāĻžāϰāĻŋ āĻāĻžāϰā§āĻ āĻ āύā§āĻ (COD & Notes)', placeholder: 'e.g. COD Amount: ā§ŗ 1130 (Dhaka inside ā§ŗ70)' }
+ ],
+ promptTemplate: `Parse this customer order and structure it cleanly for Steadfast / Pathao Courier API and Print Packing Sticker:
+Order Text: {{field1}}
+COD Info: {{field2}}
+
+Generate:
+1. Structured CSV/JSON Format (Name, Phone, Address, COD, Instruction)
+2. Clean Printable Packing Slip in Bengali.`
+ }
+];
+
+// --- Mini-Apps Hub Logic ---
+let activeCategory = 'all';
+
+function initMiniAppsHub() {
+ renderMiniApps();
+
+ const filterChips = document.querySelectorAll('.filter-chip');
+ filterChips.forEach(chip => {
+ chip.addEventListener('click', () => {
+ filterChips.forEach(c => c.classList.remove('active'));
+ chip.classList.add('active');
+ activeCategory = chip.getAttribute('data-cat');
+ renderMiniApps();
+ });
+ });
+}
+
+function getCustomMiniApps() {
+ return JSON.parse(localStorage.getItem('opal_custom_apps') || '[]');
+}
+
+function renderMiniApps() {
+ const container = document.getElementById('appsGridContainer');
+ if (!container) return;
+
+ const customApps = getCustomMiniApps();
+ const allApps = [...PREBUILT_MINI_APPS, ...customApps];
+
+ const filtered = activeCategory === 'all'
+ ? allApps
+ : (activeCategory === 'custom' ? customApps : allApps.filter(a => a.category === activeCategory));
+
+ if (filtered.length === 0) {
+ container.innerHTML = `
+
+
đ
+
No mini-apps found in this category. Click "+ Create App" to build one!
+
+ `;
+ return;
+ }
+
+ container.innerHTML = filtered.map(app => `
+
+
+
${app.emoji || 'đ'}
+
${app.badge || 'CUSTOM'}
+
+
+
${app.name}
+
${app.desc || 'AI powered dynamic mini-app for client dashboard.'}
+
+
+
+ `).join('');
+}
+
+// --- Visual Mini-App Builder ---
+function initMiniAppBuilder() {
+ const nameInput = document.getElementById('builderAppName');
+ const emojiInput = document.getElementById('builderAppEmoji');
+ const catSelect = document.getElementById('builderCategory');
+ const modelSelect = document.getElementById('builderModel');
+ const descInput = document.getElementById('builderDesc');
+ const f1Input = document.getElementById('builderField1');
+ const f2Input = document.getElementById('builderField2');
+ const templateInput = document.getElementById('builderPromptTemplate');
+ const saveBtn = document.getElementById('saveMiniAppBtn');
+ const testBtn = document.getElementById('testMiniAppBtn');
+ const statusMsg = document.getElementById('builderStatusMsg');
+
+ if (!saveBtn) return;
+
+ saveBtn.addEventListener('click', () => {
+ const name = nameInput.value.trim();
+ if (!name) {
+ alert('Please enter a Mini-App name');
+ return;
+ }
+
+ const newApp = {
+ id: 'custom_' + Date.now(),
+ name: name,
+ emoji: emojiInput.value.trim() || 'đ',
+ category: catSelect.value,
+ model: modelSelect.value,
+ badge: 'CUSTOM APP',
+ desc: descInput.value.trim() || 'Custom client dashboard mini-app.',
+ steps: 'Custom âĸ Gemini AI',
+ fields: [
+ { id: 'field1', label: f1Input.value.trim() || 'Field 1', placeholder: 'Enter ' + (f1Input.value.trim() || 'Field 1') },
+ { id: 'field2', label: f2Input.value.trim() || 'Field 2', placeholder: 'Enter ' + (f2Input.value.trim() || 'Field 2') }
+ ],
+ promptTemplate: templateInput.value.trim(),
+ createdAt: new Date().toISOString()
+ };
+
+ const existing = getCustomMiniApps();
+ existing.unshift(newApp);
+ localStorage.setItem('opal_custom_apps', JSON.stringify(existing));
+
+ statusMsg.classList.remove('hidden');
+ statusMsg.style.color = '#10B981';
+ statusMsg.textContent = `đ Mini-App "${name}" successfully saved & added to Hub!`;
+
+ renderMiniApps();
+ setTimeout(() => {
+ switchToTab('tabApps');
+ statusMsg.classList.add('hidden');
+ }, 1200);
+ });
+
+ if (testBtn) {
+ testBtn.addEventListener('click', () => {
+ const name = nameInput.value.trim() || 'Test Mini-App';
+ const tempApp = {
+ id: 'test_temp',
+ name: name,
+ emoji: emojiInput.value.trim() || 'đ§Ē',
+ desc: descInput.value.trim() || 'Testing custom app runner',
+ fields: [
+ { id: 'field1', label: f1Input.value.trim() || 'Field 1', placeholder: 'Input 1' },
+ { id: 'field2', label: f2Input.value.trim() || 'Field 2', placeholder: 'Input 2' }
+ ],
+ promptTemplate: templateInput.value.trim(),
+ model: modelSelect.value
+ };
+ launchRunnerWithApp(tempApp);
+ });
+ }
+}
+
+// --- Interactive Mini-App Runner (Modal) ---
+let currentRunningApp = null;
+
+window.openAppRunner = function(appId) {
+ if (appId === 'gt_studio') {
+ switchToTab('tabPosterLab');
+ return;
+ }
+ if (appId === 'inbox_ai') {
+ switchToTab('tabInboxAI');
+ return;
+ }
+
+ const allApps = [...PREBUILT_MINI_APPS, ...getCustomMiniApps()];
+ const app = allApps.find(a => a.id === appId);
+ if (!app) return;
+
+ launchRunnerWithApp(app);
+};
+
+function launchRunnerWithApp(app) {
+ currentRunningApp = app;
+ const modal = document.getElementById('appRunnerModal');
+ const title = document.getElementById('runnerModalTitle');
+ const desc = document.getElementById('runnerModalDesc');
+ const formBox = document.getElementById('runnerDynamicForm');
+ const resultBox = document.getElementById('runnerResultBox');
+ const statusMsg = document.getElementById('runnerStatusMsg');
+
+ title.innerHTML = `${app.emoji || 'đ'} ${app.name}`;
+ desc.textContent = app.desc || '';
+ resultBox.classList.add('hidden');
+ statusMsg.classList.add('hidden');
+
+ const fields = app.fields || [
+ { id: 'field1', label: 'Input 1', placeholder: 'Enter details...' },
+ { id: 'field2', label: 'Input 2', placeholder: 'Enter additional context...' }
+ ];
+
+ formBox.innerHTML = fields.map((f, i) => `
+
+ ${f.label}:
+
+
+ `).join('');
+
+ modal.classList.remove('hidden');
+}
+
+window.closeAppRunner = function() {
+ document.getElementById('appRunnerModal').classList.add('hidden');
+ currentRunningApp = null;
+};
+
+// Handle Mini-App Execution
+document.addEventListener('DOMContentLoaded', () => {
+ const execBtn = document.getElementById('runnerExecuteBtn');
+ if (execBtn) {
+ execBtn.addEventListener('click', async () => {
+ if (!currentRunningApp) return;
+
+ const statusMsg = document.getElementById('runnerStatusMsg');
+ const resultBox = document.getElementById('runnerResultBox');
+ const resultContent = document.getElementById('runnerResultContent');
+
+ statusMsg.classList.remove('hidden');
+ statusMsg.style.color = '#C084FC';
+ statusMsg.textContent = 'âŗ Processing with Google Gemini 2.0 AI...';
+ execBtn.disabled = true;
+
+ // Extract form values
+ const inputs = document.querySelectorAll('.runner-input');
+ let prompt = currentRunningApp.promptTemplate || 'Generate helpful response for the user.';
+
+ inputs.forEach(input => {
+ const varName = input.getAttribute('data-var');
+ const val = input.value.trim() || 'N/A';
+ const regex = new RegExp(`{{${varName}}}`, 'g');
+ prompt = prompt.replace(regex, val);
+ });
+
+ const apiKey = localStorage.getItem('opal_gemini_api_key') || '';
+ const model = currentRunningApp.model || localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash';
+
+ try {
+ let output = '';
+ if (apiKey) {
+ output = await callGeminiApi(apiKey, model, prompt);
+ } else {
+ // Dynamic intelligent fallback
+ output = `⨠[Google Opal AI Offline Mode]\n\n` +
+ `Generated Output for: ${currentRunningApp.name}\n` +
+ `----------------------------------------\n` +
+ `ā§§. āĻ
āĻĢāĻžāϰ āĻ āĻāĻžāϏā§āĻāĻŽāĻžāϰ āϰā§āϏāĻĒāύā§āϏ āĻĒā§āϰāϏā§āϤā§āϤ āĻāϰāĻž āĻšā§ā§āĻā§āĨ¤\n` +
+ `⧍. ā§§ā§Ļā§Ļ% āĻā§āύā§āĻāύ āĻā§ā§āĻžāϞāĻŋāĻāĻŋ āĻ āϏā§āĻĒāĻžāϰāĻĢāĻžāϏā§āĻ āĻĄā§āϞāĻŋāĻāĻžāϰāĻŋ āύāĻŋāĻļā§āĻāĻŋāϤ āĻāϰā§āύāĨ¤\n` +
+ `ā§Š. āϏāϰāĻžāϏāϰāĻŋ āϝā§āĻāĻžāϝā§āĻ: https://wa.me/8801712426871\n\n` +
+ `đĄ āĻāĻŋāĻĒāϏ: Settings-āĻ āĻāĻŋā§ā§ āĻāĻĒāύāĻžāϰ Google Gemini API Key āĻŦāϏāĻžāϞ⧠āϰāĻŋā§ā§āϞ-āĻāĻžāĻāĻŽ āĻāύāϞāĻŋāĻŽāĻŋāĻā§āĻĄ āϞāĻžāĻāĻ āϰā§āϏāĻĒāύā§āϏ āĻĒāĻžāĻŦā§āύ!`;
+ }
+
+ resultContent.textContent = output.trim();
+ resultBox.classList.remove('hidden');
+ statusMsg.classList.add('hidden');
+
+ // Save to History
+ saveGenerationToHistory({
+ type: currentRunningApp.name,
+ title: currentRunningApp.name,
+ price: 'AI Generation',
+ date: new Date().toLocaleTimeString()
+ });
+
+ } catch (err) {
+ statusMsg.style.color = '#EF4444';
+ statusMsg.textContent = `â Error: ${err.message}`;
+ }
+
+ execBtn.disabled = false;
+ });
+ }
+});
+
+// --- 1-Click Client Dashboard Embed Modal ---
+window.openEmbedModal = function(appId) {
+ const modal = document.getElementById('embedModal');
+ const title = document.getElementById('embedModalTitle');
+ const iframeBox = document.getElementById('embedIframeCode');
+ const apiUrlBox = document.getElementById('embedApiUrl');
+
+ const allApps = [...PREBUILT_MINI_APPS, ...getCustomMiniApps()];
+ const app = allApps.find(a => a.id === appId) || { name: 'Opal Mini-App', id: appId };
+
+ title.innerHTML = `đĻ Embed "${app.name}" in Client Dashboard`;
+
+ const baseUrl = 'https://opal.auraajenticai.cloud';
+ iframeBox.textContent = ``;
+ apiUrlBox.textContent = `${baseUrl}/api/v1/mini-app/run?app_id=${app.id}`;
+
+ modal.classList.remove('hidden');
+};
+
+window.closeEmbedModal = function() {
+ document.getElementById('embedModal').classList.add('hidden');
+};
+
+// --- Masterpiece 3D Canvas Poster Engine ---
+let selectedStageTheme = 'obsidian_gold';
+
function initPosterLab() {
const productInput = document.getElementById('productInput');
const productPrice = document.getElementById('productPrice');
+ const customBadgeInput = document.getElementById('customBadgeInput');
+ const aspectRatioSelect = document.getElementById('aspectRatioSelect');
+ const brandSelect = document.getElementById('brandWatermarkSelect');
const photoFileInput = document.getElementById('photoFileInput');
const clearPhotoBtn = document.getElementById('clearPhotoBtn');
const photoPreviewContainer = document.getElementById('photoPreviewContainer');
@@ -137,9 +616,20 @@ function initPosterLab() {
const posterStatusText = document.getElementById('posterStatusText');
const posterResultCard = document.getElementById('posterResultCard');
const posterCanvas = document.getElementById('posterCanvas');
+ const canvasContainer = document.getElementById('canvasContainer');
const downloadPosterBtn = document.getElementById('downloadPosterBtn');
const shareWhatsAppBtn = document.getElementById('shareWhatsAppBtn');
+ // Stage Theme Selector
+ const themeChips = document.querySelectorAll('#themeSelectorGroup .theme-chip');
+ themeChips.forEach(chip => {
+ chip.addEventListener('click', () => {
+ themeChips.forEach(c => c.classList.remove('active'));
+ chip.classList.add('active');
+ selectedStageTheme = chip.getAttribute('data-theme');
+ });
+ });
+
let attachedImageBitmap = null;
// Photo Input change
@@ -171,9 +661,13 @@ function initPosterLab() {
generatePosterBtn.addEventListener('click', async () => {
const inputVal = productInput.value.trim();
const priceVal = productPrice.value.trim();
+ const badgeText = customBadgeInput ? customBadgeInput.value.trim() : 'đ°đˇ 100% Original Korea';
+ const aspect = aspectRatioSelect ? aspectRatioSelect.value : '1:1';
+ const brand = brandSelect ? brandSelect.value : 'glamourstouch';
posterStatusText.classList.remove('hidden');
- posterStatusText.textContent = 'âŗ Resolving product image & rendering 3D gold stage...';
+ posterStatusText.style.color = '#F59E0B';
+ posterStatusText.textContent = 'âŗ Rendering 3D stage with GPU canvas blend...';
generatePosterBtn.disabled = true;
try {
@@ -181,27 +675,34 @@ function initPosterLab() {
let title = "Authentic Korean K-Beauty Care";
if (!productImg) {
- // Universal Product / Image Link Resolver
const resolved = await resolveProductUrl(inputVal);
if (resolved && resolved.imageUrl) {
title = resolved.title || inputVal;
productImg = await loadImage(resolved.imageUrl);
} else {
- // Fallback packshot image
- const fallbackUrl = `https://image.pollinations.ai/prompt/${encodeURIComponent(inputVal + " luxury cosmetic container jar bottle on podium 8k")}?width=1024&height=1024&nologo=true`;
+ const fallbackUrl = `https://images.unsplash.com/photo-1556228720-195a672e8a03?w=800&h=800&fit=crop`;
productImg = await loadImage(fallbackUrl);
title = inputVal;
}
}
if (productImg) {
- // Render True 3D Poster on Canvas
- await render3DCommercialPoster(posterCanvas, productImg, title, priceVal);
+ if (aspect === '9:16') {
+ posterCanvas.width = 1080;
+ posterCanvas.height = 1920;
+ canvasContainer.classList.add('aspect-reels');
+ } else {
+ posterCanvas.width = 1080;
+ posterCanvas.height = 1080;
+ canvasContainer.classList.remove('aspect-reels');
+ }
+
+ await render3DCommercialPoster(posterCanvas, productImg, title, priceVal, badgeText, selectedStageTheme, aspect, brand);
+
posterResultCard.classList.remove('hidden');
posterStatusText.classList.add('hidden');
posterResultCard.scrollIntoView({ behavior: 'smooth' });
- // Save to History
saveGenerationToHistory({
type: '3D Poster',
title: title,
@@ -209,7 +710,7 @@ function initPosterLab() {
date: new Date().toLocaleTimeString()
});
} else {
- posterStatusText.textContent = 'â Failed to load product image. Please check link.';
+ posterStatusText.textContent = 'â Failed to load product image.';
}
} catch (err) {
posterStatusText.textContent = `â Error: ${err.message}`;
@@ -218,21 +719,21 @@ function initPosterLab() {
generatePosterBtn.disabled = false;
});
- // Download Button (Save to iPhone Camera Roll)
+ // Download
downloadPosterBtn.addEventListener('click', () => {
const link = document.createElement('a');
- link.download = `GT_Poster_${Date.now()}.jpg`;
+ link.download = `Opal_3D_Poster_${Date.now()}.jpg`;
link.href = posterCanvas.toDataURL('image/jpeg', 0.95);
link.click();
});
- // WhatsApp Order Button
+ // WhatsApp
shareWhatsAppBtn.addEventListener('click', () => {
- const text = encodeURIComponent(`⨠100% Authentic Korean Skincare Order!\nProduct: ${productInput.value}\nPrice: ${productPrice.value}\nWebsite: https://glamourstouch.com`);
+ const text = encodeURIComponent(`⨠100% Authentic Product Order!\nProduct: ${productInput.value}\nPrice: ${productPrice.value}\nWebsite: https://glamourstouch.com`);
window.open(`https://wa.me/8801712426871?text=${text}`, '_blank');
});
- // 1-Click Auto Post to Facebook Page (via n8n Webhook)
+ // n8n Auto-Post Webhook
const autoPostN8nBtn = document.getElementById('autoPostN8nBtn');
const n8nPostStatusMsg = document.getElementById('n8nPostStatusMsg');
@@ -240,22 +741,22 @@ function initPosterLab() {
autoPostN8nBtn.addEventListener('click', async () => {
const webhookUrl = localStorage.getItem('opal_n8n_webhook');
if (!webhookUrl) {
- alert('â ī¸ Please set your n8n Webhook URL in Settings first!');
- document.querySelector('.nav-item[data-tab="tabSettings"]').click();
+ alert('â ī¸ Please configure n8n Webhook URL in Settings first!');
+ switchToTab('tabSettings');
return;
}
n8nPostStatusMsg.classList.remove('hidden');
n8nPostStatusMsg.style.color = '#38BDF8';
- n8nPostStatusMsg.textContent = 'âŗ Sending 3D poster & caption to n8n Webhook...';
+ n8nPostStatusMsg.textContent = 'âŗ Sending 3D poster & copy to n8n Webhook...';
autoPostN8nBtn.disabled = true;
try {
const posterDataUrl = posterCanvas.toDataURL('image/jpeg', 0.92);
const payload = {
- title: productInput.value.trim() || 'Authentic Korean K-Beauty Skincare',
+ title: productInput.value.trim() || 'Authentic K-Beauty Skincare',
price: productPrice.value.trim() || 'ā§ŗ ā§§,ā§Ļā§Ŧā§Ļ',
- caption: `⨠${productInput.value.trim()} â¨\n\nđ° āĻ
āĻĢāĻžāϰ āĻŽā§āϞā§āϝ: ${productPrice.value.trim()}\nđĄī¸ ā§§ā§Ļā§Ļ% āĻ
āϰāĻŋāĻāĻŋāύāĻžāϞ āĻā§āϰāĻŋā§āĻžāύ āĻāϏāĻŽā§āĻāĻŋāĻā§āϏ āĻā§āϝāĻžāϰāĻžāύā§āĻāĻŋ!\n\nđŦ āϏāϰāĻžāϏāϰāĻŋ WhatsApp āĻ āĻ
āϰā§āĻĄāĻžāϰ āĻāϰāϤ⧠āĻāύāĻŦāĻā§āϏ āĻāϰā§āύ:\nđ https://wa.me/8801712426871\nđ āĻšāĻāϞāĻžāĻāύ: 01712-426871\nđ āĻā§ā§āĻŦāϏāĻžāĻāĻ: https://glamourstouch.com`,
+ caption: `⨠${productInput.value.trim()} â¨\n\nđ° āĻ
āĻĢāĻžāϰ āĻŽā§āϞā§āϝ: ${productPrice.value.trim()}\nđĄī¸ ā§§ā§Ļā§Ļ% āĻ
āϰāĻŋāĻāĻŋāύāĻžāϞ āĻā§āϝāĻžāϰāĻžāύā§āĻāĻŋ!\n\nđŦ āϏāϰāĻžāϏāϰāĻŋ WhatsApp āĻ āĻ
āϰā§āĻĄāĻžāϰ āĻāϰāϤ⧠āĻāύāĻŦāĻā§āϏ āĻāϰā§āύ:\nđ https://wa.me/8801712426871\nđ āĻšāĻāϞāĻžāĻāύ: 01712-426871\nđ āĻā§ā§āĻŦāϏāĻžāĻāĻ: https://glamourstouch.com`,
image_url: posterDataUrl,
store: "Glamour's Touch",
website: "https://glamourstouch.com",
@@ -287,21 +788,19 @@ function initPosterLab() {
}
}
-// Universal Link & Product Name Resolver
+// Universal Link Resolver
async function resolveProductUrl(input) {
if (!input) return null;
if (input.match(/\.(jpeg|jpg|gif|png|webp)($|\?)/i)) {
- return { title: 'Authentic Korean Product', imageUrl: input };
+ return { title: 'Authentic Product', imageUrl: input };
}
const key = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZtY2x0cmpudXZ1b29hcmt2dWZuIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzUxMzY3MDQsImV4cCI6MjA5MDcxMjcwNH0.PkSgBAZx41X4sZurfyOdxCVa01hkKTkyBhVkGzx_4y4';
- // 1. Glamour's Touch Supabase UUID regex
const uuidMatch = input.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/);
if (uuidMatch) {
- const productId = uuidMatch[0];
try {
- const res = await fetch(`https://fmcltrjnuvuooarkvufn.supabase.co/rest/v1/products?id=eq.${productId}&select=*`, {
+ const res = await fetch(`https://fmcltrjnuvuooarkvufn.supabase.co/rest/v1/products?id=eq.${uuidMatch[0]}&select=*`, {
headers: { 'apikey': key, 'Authorization': `Bearer ${key}` }
});
const data = await res.json();
@@ -318,7 +817,6 @@ async function resolveProductUrl(input) {
}
}
- // 2. Search Supabase by product Name
try {
const cleanQuery = encodeURIComponent(input.replace(/https?:\/\/[^\s]+/g, '').trim().slice(0, 30));
if (cleanQuery) {
@@ -339,25 +837,7 @@ async function resolveProductUrl(input) {
console.warn('GT Supabase search by name failed:', e);
}
- // 3. Fallback to 1st featured product in catalog
- try {
- const res = await fetch(`https://fmcltrjnuvuooarkvufn.supabase.co/rest/v1/products?in_stock=eq.true&image=not.is.null&limit=1`, {
- headers: { 'apikey': key, 'Authorization': `Bearer ${key}` }
- });
- const data = await res.json();
- if (data && data.length > 0) {
- const p = data[0];
- return {
- title: p.name || input,
- price: p.price ? 'ā§ŗ ' + p.price : '',
- imageUrl: p.image || ''
- };
- }
- } catch (e) {
- console.warn('Fallback failed:', e);
- }
-
- return { title: input, imageUrl: 'https://fmcltrjnuvuooarkvufn.supabase.co/storage/v1/object/public/products/product-images/koba-74d48daa-7695-41f9-8f10-9e5fb4f0e2d9.jpg' };
+ return { title: input, imageUrl: 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=800&h=800&fit=crop' };
}
function loadImage(src) {
@@ -374,130 +854,140 @@ function loadImage(src) {
const fallbackImg = new Image();
fallbackImg.crossOrigin = 'anonymous';
fallbackImg.onload = () => resolve(fallbackImg);
- fallbackImg.onerror = () => reject(new Error('Could not load image across CORS'));
+ fallbackImg.onerror = () => reject(new Error('Could not load image'));
fallbackImg.src = src;
};
img.src = loadUrl;
});
}
-// --- 4. Masterpiece 3D Canvas Rendering Engine ---
-async function render3DCommercialPoster(canvas, productImg, title, price) {
+// --- 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');
- const w = 1080;
- const h = 1080;
+ const w = canvas.width;
+ const h = canvas.height;
- // 1. Midnight Obsidian Background
- const bgGrad = ctx.createRadialGradient(w / 2, h * 0.44, 50, w / 2, h * 0.44, w * 0.72);
- bgGrad.addColorStop(0, '#18212D');
- bgGrad.addColorStop(0.6, '#0C1117');
- bgGrad.addColorStop(1, '#05080C');
+ // Theme palettes
+ const THEMES = {
+ obsidian_gold: {
+ bgCenter: '#18212D', bgMid: '#0C1117', bgOuter: '#05080C',
+ spotlightColor: '#FFFFFF', goldRim: '#E5B83A', ringColor: '#66E5B83A',
+ pedestalTop: '#FFFFFF', pedestalMid: '#EBF2F8', pedestalDark: '#1B2533',
+ headerTitle: '⨠GLAMOUR\'S TOUCH â¨', headerSub: 'ëíë¯ŧęĩ íëĻŦ미ė ė¤í¨āĻā§āϝāĻŧāĻžāϰ âĸ 100% AUTHENTIC KOREAN COSMETICS'
+ },
+ rose_quartz: {
+ bgCenter: '#2D1824', bgMid: '#170C13', bgOuter: '#0C0509',
+ spotlightColor: '#FFF0F5', goldRim: '#F472B6', ringColor: '#F472B666',
+ pedestalTop: '#FFF5F8', pedestalMid: '#FCE7F3', pedestalDark: '#371829',
+ headerTitle: 'đ¸ SNEHALATA AURA đ¸', headerSub: 'PREMIUM LUXURY LIFESTYLE âĸ 100% AUTHENTIC BEAUTY'
+ },
+ cyber_neon: {
+ bgCenter: '#0F172A', bgMid: '#060E1A', bgOuter: '#02060D',
+ spotlightColor: '#E0F2FE', goldRim: '#06B6D4', ringColor: '#06B6D466',
+ pedestalTop: '#F0FDF4', pedestalMid: '#CFFAFE', pedestalDark: '#082F49',
+ headerTitle: 'đ AURA ENTERPRISE đ', headerSub: 'NEXT-GEN AGENTIC COMMERCE âĸ AI EMPOWERED'
+ },
+ botanical_eco: {
+ bgCenter: '#14281E', bgMid: '#0B1711', bgOuter: '#040B07',
+ spotlightColor: '#F0FDF4', goldRim: '#10B981', ringColor: '#10B98166',
+ pedestalTop: '#F7FEE7', pedestalMid: '#DCFCE7', pedestalDark: '#064E3B',
+ headerTitle: 'đŋ PURE BOTANICAL CARE đŋ', headerSub: 'ORGANIC DERMATOLOGY âĸ CLINICALLY PROVEN'
+ }
+ };
+
+ const currentTheme = THEMES[theme] || THEMES.obsidian_gold;
+
+ // 1. Background Radial Gradient
+ const stageCy = aspect === '9:16' ? h * 0.46 : h * 0.44;
+ const bgGrad = ctx.createRadialGradient(w / 2, stageCy, 50, w / 2, stageCy, w * 0.75);
+ bgGrad.addColorStop(0, currentTheme.bgCenter);
+ bgGrad.addColorStop(0.6, currentTheme.bgMid);
+ bgGrad.addColorStop(1, currentTheme.bgOuter);
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, w, h);
- // 2. White-to-Midnight Luxury Radial Spotlight (Smooth Studio Blend)
+ // 2. Spotlight
const stageCx = w / 2;
- const stageCy = h * 0.44;
const stageRadius = w * 0.38;
const spotGrad = ctx.createRadialGradient(stageCx, stageCy, 0, stageCx, stageCy, stageRadius);
spotGrad.addColorStop(0, '#FFFFFF');
- spotGrad.addColorStop(0.46, '#FFFFFF');
- spotGrad.addColorStop(0.64, '#F0F5FA');
- spotGrad.addColorStop(0.82, '#8E9FAC');
- spotGrad.addColorStop(0.94, '#283545');
- spotGrad.addColorStop(1, '#0C1117');
+ spotGrad.addColorStop(0.48, '#FFFFFF');
+ spotGrad.addColorStop(0.68, currentTheme.spotlightColor);
+ spotGrad.addColorStop(0.85, '#64748B');
+ spotGrad.addColorStop(1, 'transparent');
ctx.fillStyle = spotGrad;
ctx.beginPath();
ctx.arc(stageCx, stageCy, stageRadius, 0, Math.PI * 2);
ctx.fill();
- // Luxury Gold Dashed Rings
- ctx.strokeStyle = '#66E5B83A';
+ // Glow Rings
+ ctx.strokeStyle = currentTheme.ringColor;
ctx.lineWidth = 2.5;
ctx.beginPath();
ctx.arc(stageCx, stageCy, stageRadius * 0.96, 0, Math.PI * 2);
ctx.stroke();
- ctx.strokeStyle = '#99E5B83A';
- ctx.lineWidth = 1.8;
ctx.setLineDash([12, 12]);
ctx.beginPath();
ctx.arc(stageCx, stageCy, stageRadius * 0.91, 0, Math.PI * 2);
ctx.stroke();
ctx.setLineDash([]);
- // 3. True 3D Dual-Tier Cylindrical Podium
- // --- TIER 1: Lower Base ---
- const baseTierY = 745;
+ // 3. 3D Dual-Tier Podium
+ const baseTierY = aspect === '9:16' ? 1220 : 745;
const baseTierRx = w * 0.38;
const baseTierRy = 34;
const baseTierHeight = 26;
- // Tier 1 Ground Shadow
+ // Base Shadow
const baseShadow = ctx.createRadialGradient(stageCx, baseTierY + baseTierHeight + 16, 20, stageCx, baseTierY + baseTierHeight + 16, baseTierRx * 1.15);
baseShadow.addColorStop(0, 'rgba(0,0,0,0.7)');
baseShadow.addColorStop(1, 'transparent');
ctx.fillStyle = baseShadow;
drawOval(ctx, stageCx, baseTierY + baseTierHeight + baseTierRy * 0.6, baseTierRx * 1.15, baseTierRy * 1.6);
- // Tier 1 Cylinder Body
+ // Base Cylinder Body
const baseCylGrad = ctx.createLinearGradient(stageCx - baseTierRx, 0, stageCx + baseTierRx, 0);
baseCylGrad.addColorStop(0, '#090D12');
- baseCylGrad.addColorStop(0.2, '#1C2633');
- baseCylGrad.addColorStop(0.5, '#6C829E');
- baseCylGrad.addColorStop(0.8, '#1C2633');
+ baseCylGrad.addColorStop(0.5, '#475569');
baseCylGrad.addColorStop(1, '#090D12');
ctx.fillStyle = baseCylGrad;
ctx.fillRect(stageCx - baseTierRx, baseTierY, baseTierRx * 2, baseTierHeight);
drawOval(ctx, stageCx, baseTierY + baseTierHeight, baseTierRx, baseTierRy);
-
- // Tier 1 Top Cap
- const baseTopGrad = ctx.createLinearGradient(stageCx - baseTierRx, baseTierY - baseTierRy, stageCx + baseTierRx, baseTierY + baseTierRy);
- baseTopGrad.addColorStop(0, '#222D3A');
- baseTopGrad.addColorStop(1, '#0B1017');
- ctx.fillStyle = baseTopGrad;
drawOval(ctx, stageCx, baseTierY, baseTierRx, baseTierRy);
- // --- TIER 2: Upper Product Pedestal ---
- const topTierY = 708;
+ // Top Tier
+ const topTierY = aspect === '9:16' ? 1180 : 708;
const topTierRx = w * 0.31;
const topTierRy = 28;
const topTierHeight = 34;
- // Tier 2 Cylinder Body
const topCylGrad = ctx.createLinearGradient(stageCx - topTierRx, 0, stageCx + topTierRx, 0);
topCylGrad.addColorStop(0, '#070A0E');
- topCylGrad.addColorStop(0.25, '#18212C');
- topCylGrad.addColorStop(0.5, '#F5E6AB');
- topCylGrad.addColorStop(0.75, '#18212C');
+ topCylGrad.addColorStop(0.5, currentTheme.goldRim);
topCylGrad.addColorStop(1, '#070A0E');
ctx.fillStyle = topCylGrad;
ctx.fillRect(stageCx - topTierRx, topTierY, topTierRx * 2, topTierHeight);
drawOval(ctx, stageCx, topTierY + topTierHeight, topTierRx, topTierRy);
- // Tier 2 Top Surface (Glossy White/Platinum Cap)
+ // Top Surface Cap
const topSurfaceGrad = ctx.createRadialGradient(stageCx, topTierY, 0, stageCx, topTierY, topTierRx);
- topSurfaceGrad.addColorStop(0, '#FFFFFF');
- topSurfaceGrad.addColorStop(0.45, '#EBF2F8');
- topSurfaceGrad.addColorStop(0.8, '#899EAF');
- topSurfaceGrad.addColorStop(1, '#1B2533');
+ topSurfaceGrad.addColorStop(0, currentTheme.pedestalTop);
+ topSurfaceGrad.addColorStop(0.5, currentTheme.pedestalMid);
+ topSurfaceGrad.addColorStop(1, currentTheme.pedestalDark);
ctx.fillStyle = topSurfaceGrad;
drawOval(ctx, stageCx, topTierY, topTierRx, topTierRy);
- // Tier 2 Top Gold Beveled Rim
- const goldRimGrad = ctx.createLinearGradient(stageCx - topTierRx, topTierY, stageCx + topTierRx, topTierY);
- goldRimGrad.addColorStop(0, '#B38728');
- goldRimGrad.addColorStop(0.5, '#FFFFFF');
- goldRimGrad.addColorStop(1, '#AA771C');
- ctx.strokeStyle = goldRimGrad;
+ // Gold Beveled Rim
+ ctx.strokeStyle = currentTheme.goldRim;
ctx.lineWidth = 3.5;
strokeOval(ctx, stageCx, topTierY, topTierRx, topTierRy);
- // 4. Hero Product Scaling & Placement
- const targetH = 500;
- const targetW = 520;
+ // 4. Product Scaling & Placement
+ const targetH = aspect === '9:16' ? 560 : 500;
+ const targetW = aspect === '9:16' ? 580 : 520;
const srcW = productImg.width || 500;
const srcH = productImg.height || 500;
const scale = Math.min(targetW / srcW, targetH / srcH);
@@ -506,7 +996,7 @@ async function render3DCommercialPoster(canvas, productImg, title, price) {
const destLeft = (w - destW) / 2;
const destTop = topTierY - destH + 18;
- // 4.1 Glossy Mirror Product Reflection (Multiply blended)
+ // Floor Reflection (Multiply blend)
ctx.save();
ctx.globalCompositeOperation = 'multiply';
ctx.globalAlpha = 0.25;
@@ -522,48 +1012,50 @@ async function render3DCommercialPoster(canvas, productImg, title, price) {
ctx.fillStyle = contactShadow;
drawOval(ctx, stageCx, topTierY + 2, destW * 0.38, 12);
- // Draw Hero Product with Multiply Blend Mode (removes white background completely)
+ // Product Image (Multiply blended to wipe white background)
ctx.save();
ctx.globalCompositeOperation = 'multiply';
ctx.drawImage(productImg, destLeft, destTop, destW, destH);
ctx.restore();
// 5. Value Trust Badges
- drawPillBadge(ctx, 40, 240, 'đ°đˇ 100% Original Korea', true);
- drawPillBadge(ctx, 40, 315, 'đŋ Skin Friendly Formula', true);
- drawPillBadge(ctx, w - 40, 240, '⥠Fast Delivery BD', false);
- drawPillBadge(ctx, w - 40, 315, 'đĄī¸ Authentic Guarantee', false);
+ const badgeY1 = aspect === '9:16' ? 340 : 240;
+ const badgeY2 = badgeY1 + 75;
+ drawPillBadge(ctx, 40, badgeY1, badgeText, true, currentTheme.goldRim);
+ drawPillBadge(ctx, 40, badgeY2, 'đŋ Skin Friendly Formula', true, currentTheme.goldRim);
+ drawPillBadge(ctx, w - 40, badgeY1, '⥠Fast Delivery BD', false, currentTheme.goldRim);
+ drawPillBadge(ctx, w - 40, badgeY2, 'đĄī¸ Authentic Guarantee', false, currentTheme.goldRim);
- // 6. Top Header Ribbon
+ // 6. Header Ribbon
ctx.fillStyle = '#F2090D12';
ctx.fillRect(0, 0, w, 92);
- ctx.strokeStyle = '#E5B83A';
+ ctx.strokeStyle = currentTheme.goldRim;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(0, 92);
ctx.lineTo(w, 92);
ctx.stroke();
- ctx.fillStyle = '#E5B83A';
- ctx.font = 'bold 34px Plus Jakarta Sans, sans-serif';
+ ctx.fillStyle = currentTheme.goldRim;
+ ctx.font = 'bold 32px Plus Jakarta Sans, sans-serif';
ctx.textAlign = 'center';
- ctx.fillText('⨠GLAMOUR\'S TOUCH â¨', w / 2, 52);
+ ctx.fillText(currentTheme.headerTitle, w / 2, 52);
ctx.fillStyle = '#FFF4D0';
- ctx.font = '17px Plus Jakarta Sans, sans-serif';
- ctx.fillText('ëíë¯ŧęĩ íëĻŦ미ė ė¤í¨ėŧė´ âĸ 100% AUTHENTIC KOREAN COSMETICS', w / 2, 80);
+ ctx.font = '16px Plus Jakarta Sans, sans-serif';
+ ctx.fillText(currentTheme.headerSub, w / 2, 80);
// 7. Product Title Card
const cleanTitle = title.length > 60 ? title.substring(0, 58) + '...' : title;
- const titleCardTop = h - 235;
- const titleCardBottom = h - 128;
+ const titleCardTop = h - (aspect === '9:16' ? 320 : 235);
+ const titleCardBottom = h - (aspect === '9:16' ? 150 : 128);
ctx.fillStyle = '#EA0B0E14';
roundRect(ctx, 28, titleCardTop, w - 56, titleCardBottom - titleCardTop, 18);
ctx.fill();
- ctx.strokeStyle = '#55E5B83A';
+ ctx.strokeStyle = currentTheme.goldRim + '88';
ctx.lineWidth = 2;
roundRect(ctx, 28, titleCardTop, w - 56, titleCardBottom - titleCardTop, 18);
ctx.stroke();
@@ -573,32 +1065,33 @@ async function render3DCommercialPoster(canvas, productImg, title, price) {
ctx.textAlign = 'center';
ctx.fillText(cleanTitle, w / 2, titleCardTop + 42);
- ctx.fillStyle = '#E5B83A';
- ctx.font = 'bold 20px Hind Siliguri, sans-serif';
- ctx.fillText('āĻā§āϞāĻžāϏ āϏā§āĻāĻŋāύ āĻ āĻĻāĻžāĻāĻšā§āύ āĻāĻā§āĻā§āĻŦāϞāϤāĻžāϰ āĻā§āϰāĻŋā§āĻžāύ āϏāĻŋāĻā§āϰā§āĻ âĸ āϏāϰāĻžāϏāϰāĻŋ āĻā§āϰāĻŋā§āĻž āĻĨā§āĻā§ āĻāĻŽāĻĻāĻžāύāĻŋāĻā§āϤ', w / 2, titleCardTop + 78);
+ ctx.fillStyle = currentTheme.goldRim;
+ ctx.font = 'bold 21px Hind Siliguri, sans-serif';
+ ctx.fillText(`${price} âĸ ā§§ā§Ļā§Ļ% āĻāϏāϞ āĻā§āϝāĻžāϰāĻžāύā§āĻāĻŋ âĸ āĻĻā§āϰā§āϤ āĻšā§āĻŽ āĻĄā§āϞāĻŋāĻāĻžāϰāĻŋ`, w / 2, titleCardTop + 80);
- // 8. Bottom WhatsApp Order Ribbon (Green Banner)
- const footerTop = h - 118;
+ // 8. Bottom WhatsApp Banner
+ const footerHeight = aspect === '9:16' ? 140 : 118;
+ const footerTop = h - footerHeight;
const footerGrad = ctx.createLinearGradient(0, footerTop, w, h);
footerGrad.addColorStop(0, '#25D366');
footerGrad.addColorStop(1, '#1EB855');
ctx.fillStyle = footerGrad;
- ctx.fillRect(0, footerTop, w, 118);
+ ctx.fillRect(0, footerTop, w, footerHeight);
ctx.fillStyle = '#FFFFFF';
ctx.font = 'bold 32px Plus Jakarta Sans, sans-serif';
ctx.textAlign = 'left';
- ctx.fillText('đŦ WhatsApp: 01712-426871', 35, footerTop + 48);
+ ctx.fillText('đŦ WhatsApp Order: 01712-426871', 35, footerTop + 48);
ctx.fillStyle = '#053314';
- ctx.font = 'bold 21px Plus Jakarta Sans, sans-serif';
- ctx.fillText('đ Website: glamourstouch.com âĸ đ Call: 01712-426871', 35, footerTop + 90);
+ ctx.font = 'bold 20px Plus Jakarta Sans, sans-serif';
+ ctx.fillText('đ Website: glamourstouch.com âĸ đ Hotline: 01712-426871', 35, footerTop + 90);
- // 8.1 Render Scannable QR Code on Bottom Right
+ // QR Code
await drawWhatsAppQrOnCanvas(ctx, w - 130, footerTop + 11, 96);
- // 9. Outer Gold Frame Edge
- ctx.strokeStyle = '#88E5B83A';
+ // Outer Border
+ ctx.strokeStyle = currentTheme.goldRim + '99';
ctx.lineWidth = 6;
ctx.strokeRect(3, 3, w - 6, h - 6);
}
@@ -629,7 +1122,7 @@ function roundRect(ctx, x, y, width, height, radius) {
ctx.closePath();
}
-function drawPillBadge(ctx, x, y, text, isLeft) {
+function drawPillBadge(ctx, x, y, text, isLeft, goldColor = '#E5B83A') {
ctx.font = 'bold 18px Plus Jakarta Sans, Hind Siliguri, sans-serif';
const textWidth = ctx.measureText(text).width;
const pillW = textWidth + 28;
@@ -641,7 +1134,7 @@ function drawPillBadge(ctx, x, y, text, isLeft) {
roundRect(ctx, pillX, pillY, pillW, pillH, 18);
ctx.fill();
- ctx.strokeStyle = '#55E5B83A';
+ ctx.strokeStyle = goldColor + '66';
ctx.lineWidth = 1.5;
roundRect(ctx, pillX, pillY, pillW, pillH, 18);
ctx.stroke();
@@ -652,31 +1145,31 @@ function drawPillBadge(ctx, x, y, text, isLeft) {
}
async function drawWhatsAppQrOnCanvas(ctx, x, y, size) {
- // White background container
ctx.fillStyle = '#FFFFFF';
roundRect(ctx, x - 4, y - 4, size + 8, size + 8, 10);
ctx.fill();
- // Create temporary QR element
const qrContainer = document.getElementById('qrcodeHidden');
- qrContainer.innerHTML = '';
- new QRCode(qrContainer, {
- text: 'https://wa.me/8801712426871',
- width: size,
- height: size,
- colorDark: '#0B2E13',
- colorLight: '#FFFFFF',
- correctLevel: QRCode.CorrectLevel.M
- });
+ if (qrContainer) {
+ qrContainer.innerHTML = '';
+ new QRCode(qrContainer, {
+ text: 'https://wa.me/8801712426871',
+ width: size,
+ height: size,
+ colorDark: '#0B2E13',
+ colorLight: '#FFFFFF',
+ correctLevel: QRCode.CorrectLevel.M
+ });
- await new Promise(r => setTimeout(r, 80));
- const qrImg = qrContainer.querySelector('img') || qrContainer.querySelector('canvas');
- if (qrImg) {
- ctx.drawImage(qrImg, x, y, size, size);
+ await new Promise(r => setTimeout(r, 80));
+ const qrImg = qrContainer.querySelector('img') || qrContainer.querySelector('canvas');
+ if (qrImg) {
+ ctx.drawImage(qrImg, x, y, size, size);
+ }
}
}
-// --- 5. Customer Care & Inbox AI ---
+// --- Customer Care & Inbox AI Dedicated Studio ---
function initInboxAI() {
const customerMsgInput = document.getElementById('inboxCustomerMsg');
const storeContextInput = document.getElementById('inboxStoreContext');
@@ -687,11 +1180,13 @@ function initInboxAI() {
const reply2 = document.getElementById('replyText2');
const reply3 = document.getElementById('replyText3');
+ if (!generateBtn) return;
+
generateBtn.addEventListener('click', async () => {
const query = customerMsgInput.value.trim();
const storeInfo = storeContextInput.value.trim();
const apiKey = localStorage.getItem('opal_gemini_api_key') || '';
- const model = localStorage.getItem('opal_selected_model') || 'gemini-3.6-flash';
+ const model = localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash';
if (!query) {
alert('Please enter a customer message');
@@ -699,12 +1194,12 @@ function initInboxAI() {
}
statusMsg.classList.remove('hidden');
- statusMsg.textContent = 'âŗ Generating 3 sweet & polite sales replies with Gemma AI...';
+ statusMsg.textContent = 'âŗ Generating 3 sweet & polite sales replies with Gemini AI...';
generateBtn.disabled = true;
try {
if (apiKey) {
- const prompt = `A customer sent this message in Glamour's Touch inbox:
+ const prompt = `A customer sent this message in e-commerce inbox:
'${query}'
Store Info: ${storeInfo}
@@ -720,11 +1215,10 @@ Format exactly with:
const result = await callGeminiApi(apiKey, model, prompt);
const parts = result.split(/---REPLY[123]---/);
- reply1.textContent = (parts[1] || 'āĻāϏāϏāĻžāϞāĻžāĻŽā§ āĻāϞāĻžāĻāĻā§āĻŽ āĻĒā§āϰāĻŋā§ āĻāĻžāϏā§āĻāĻŽāĻžāϰ! āĻāĻŽāĻžāĻĻā§āϰ āĻā§āϞāĻžāϏ āϏā§āĻāĻŋāύ āϏāĻŋāϰāĻžāĻŽā§āϰ āĻĻāĻžāĻŽ āĻŽāĻžāϤā§āϰ ā§ŗā§§ā§Ŧā§Ļā§ĻāĨ¤ āĻāĻāĻŋ ā§§ā§Ļā§Ļ% āĻāϏāϞ āĻā§āϰāĻŋā§āĻžāύ āĻĒā§āϰāĻĄāĻžāĻā§āĻāĨ¤').trim();
+ reply1.textContent = (parts[1] || 'āĻāϏāϏāĻžāϞāĻžāĻŽā§ āĻāϞāĻžāĻāĻā§āĻŽ āĻĒā§āϰāĻŋā§ āĻāĻžāϏā§āĻāĻŽāĻžāϰ! āĻāĻŽāĻžāĻĻā§āϰ āĻĒā§āϰāĻĄāĻžāĻā§āĻā§āϰ āĻ
āĻĢāĻžāϰ āĻŽā§āϞā§āϝ āĻŽāĻžāϤā§āϰ ā§ŗā§§ā§Ļā§Ŧā§ĻāĨ¤ āĻāĻāĻŋ ā§§ā§Ļā§Ļ% āĻāϏāϞ āĻĒā§āϰāĻĄāĻžāĻā§āĻāĨ¤').trim();
reply2.textContent = (parts[2] || 'āĻ
āϰā§āĻĄāĻžāϰāĻāĻŋ āĻāύāĻĢāĻžāϰā§āĻŽ āĻāϰāϤ⧠āĻāĻĒāύāĻžāϰ āύāĻžāĻŽ, āϏāĻŽā§āĻĒā§āϰā§āĻŖ āĻ āĻŋāĻāĻžāύāĻž āĻāĻŦāĻ āĻŽā§āĻŦāĻžāĻāϞ āύāĻŽā§āĻŦāϰāĻāĻŋ āϞāĻŋāĻā§ āĻĒāĻžāĻ āĻžāύāĨ¤ āĻāĻŽāϰāĻž āĻĻā§āϰā§āϤ āĻā§āϝāĻžāĻļ āĻ
āύ āĻĄā§āϞāĻŋāĻāĻžāϰāĻŋāϤ⧠āĻĒāĻžāĻ āĻŋā§ā§ āĻĻā§āĻŦā§!').trim();
reply3.textContent = (parts[3] || 'āϏāϰāĻžāϏāϰāĻŋ āĻšā§ā§āĻžāĻāϏāĻ
ā§āϝāĻžāĻĒā§ āĻĻā§āϰā§āϤ āĻ
āϰā§āĻĄāĻžāϰ āĻāϰāϤ⧠āĻā§āϞāĻŋāĻ āĻāϰā§āύ: https://wa.me/8801712426871\nāĻšāĻāϞāĻžāĻāύ: 01712-426871').trim();
} else {
- // High quality offline fallback template
reply1.textContent = `āĻāϏāϏāĻžāϞāĻžāĻŽā§ āĻāϞāĻžāĻāĻā§āĻŽ āĻĒā§āϰāĻŋā§ āĻāĻĒā§/āĻāĻžāĻā§āĻž! đĨ°\nāĻāĻŽāĻžāĻĻā§āϰ āĻāĻžāĻā§ ā§§ā§Ļā§Ļ% āĻ
āϰāĻŋāĻāĻŋāύāĻžāϞ āĻā§āϰāĻŋā§āĻžāύ āϏā§āĻāĻŋāύāĻā§ā§āĻžāϰ āĻĒā§āϰā§āĻĄāĻžāĻā§āĻ āϰā§ā§āĻā§āĨ¤\n\nđ° āĻ
āĻĢāĻžāϰ āĻŽā§āϞā§āϝ: ā§ŗ ā§§,ā§Ļā§Ŧā§Ļ (āϰā§āĻā§āϞāĻžāϰ ā§ŗ ā§§,ā§Ēā§ā§Ļ)\nđ āĻšā§āĻŽ āĻĄā§āϞāĻŋāĻāĻžāϰāĻŋ: āĻĸāĻžāĻāĻžā§ ā§ŗā§ā§Ļ, āĻĸāĻžāĻāĻžāϰ āĻŦāĻžāĻāĻ°ā§ ā§ŗā§§ā§Šā§Ļ\n\nāĻāĻāĻŋ āύāĻŋā§āĻŽāĻŋāϤ āĻŦā§āϝāĻŦāĻšāĻžāϰ⧠āϤā§āĻŦāĻ āĻšāĻŦā§ āĻāĻžāĻā§āϰ āĻŽāϤ⧠āĻāĻāĻāĻā§ āĻ āĻĻāĻžāĻāĻšā§āύ! đ¸`;
reply2.textContent = `āĻ
āϰā§āĻĄāĻžāϰāĻāĻŋ āĻāύāĻĢāĻžāϰā§āĻŽ āĻāϰāϤ⧠āĻ
āύā§āĻā§āϰāĻš āĻāϰ⧠āĻāĻĒāύāĻžāϰ:\nā§§. āύāĻžāĻŽ\n⧍. āϏāĻŽā§āĻĒā§āϰā§āĻŖ āĻ āĻŋāĻāĻžāύāĻž (āĻā§āϞāĻž/āĻĨāĻžāύāĻž āϏāĻš)\nā§Š. āĻŽā§āĻŦāĻžāĻāϞ āύāĻŽā§āĻŦāϰ\n\nāϞāĻŋāĻā§ āĻĒāĻžāĻ āĻŋā§ā§ āĻĻāĻŋāύāĨ¤ āĻāĻŽāϰāĻž āĻĻā§āϰā§āϤ āĻĒā§āϰā§āĻĄāĻžāĻā§āĻ āĻĒāĻžāĻ āĻŋā§ā§ āĻĻā§āĻŦā§! āϧāύā§āϝāĻŦāĻžāĻĻāĨ¤ đī¸`;
reply3.textContent = `āϏāϰāĻžāϏāϰāĻŋ āĻšā§ā§āĻžāĻāϏāĻ
ā§āϝāĻžāĻĒā§ ā§§-āĻā§āϞāĻŋāĻā§ āĻ
āϰā§āĻĄāĻžāϰ āĻāϰāϤ⧠āĻāĻŽāĻžāĻĻā§āϰ āĻāύāĻŦāĻā§āϏ āĻāϰā§āύ:\nđŦ WhatsApp: https://wa.me/8801712426871\nđ āĻšāĻāϞāĻžāĻāύ: 01712-426871\nđ āĻā§ā§āĻŦāϏāĻžāĻāĻ: https://glamourstouch.com`;
@@ -741,38 +1235,44 @@ Format exactly with:
});
}
-// --- 6. History Management ---
+// --- History Management ---
function initHistory() {
const historyContainer = document.getElementById('historyListContainer');
const clearBtn = document.getElementById('clearHistoryBtn');
function renderHistory() {
+ if (!historyContainer) return;
const list = JSON.parse(localStorage.getItem('opal_history') || '[]');
if (list.length === 0) {
historyContainer.innerHTML = `
đ
-
No generations yet. Create your first 3D poster or copy!
+
No generations yet. Create your first 3D poster or mini-app generation!
`;
return;
}
historyContainer.innerHTML = list.map(item => `
-
${item.type === '3D Poster' ? 'đ¨' : 'đŦ'}
-
-
${item.title}
-
${item.price || item.type}
+
+
${item.type === '3D Poster' ? 'đ¨' : 'âĄ'}
+
${item.type}
+
+
${item.title}
+
${item.price || item.type}
+
`).join('');
}
- clearBtn.addEventListener('click', () => {
- localStorage.removeItem('opal_history');
- renderHistory();
- });
+ if (clearBtn) {
+ clearBtn.addEventListener('click', () => {
+ localStorage.removeItem('opal_history');
+ renderHistory();
+ });
+ }
renderHistory();
}
diff --git a/index.html b/index.html
index 99a90cc..c91fc35 100644
--- a/index.html
+++ b/index.html
@@ -3,14 +3,14 @@
-
Google Opal AI - iOS Studio & 3D Poster Lab
+
Google Opal AI - iOS Studio & 3D Mini-App Engine
-
+
@@ -18,20 +18,23 @@
-
+