Abmacode12's picture
import React, { useState, useRef, useEffect } from 'react';
9058b09 verified
// State management
const state = {
activeTab: 'chat',
messages: [
{
role: 'assistant',
content: "Salut ! 👋 Je suis Rosalinda, votre AI senior full-stack.\n\nJe peux générer des interfaces web professionnelles :\n• Pricing pages SaaS\n• Landing pages startup\n• Dashboard analytics\n• Boutiques e-commerce\n• Scripts vidéo pro\n\nCliquez sur un bouton rapide ou décrivez ce que vous voulez générer."
}
],
generatedCode: { html: '', css: '', js: '' },
loading: false,
theme: 'brand-blue',
industry: 'app-hosting',
activeQuickGen: null
};
// DOM elements
const elements = {
chatPanel: document.getElementById('chat-panel'),
codePanel: document.getElementById('code-panel'),
jsonPanel: document.getElementById('json-panel'),
chatTab: document.getElementById('chat-tab'),
codeTab: document.getElementById('code-tab'),
jsonTab: document.getElementById('json-tab'),
messagesContainer: document.getElementById('messages-container'),
messageInput: document.getElementById('message-input'),
sendButton: document.getElementById('send-button'),
quickGenButtons: document.getElementById('quick-gen-buttons'),
themeSelect: document.getElementById('theme-select'),
industrySelect: document.getElementById('industry-select'),
htmlCode: document.getElementById('html-code'),
cssCode: document.getElementById('css-code'),
jsCode: document.getElementById('js-code'),
jsonOutput: document.getElementById('json-output'),
copyButton: document.getElementById('copy-button'),
downloadButton: document.getElementById('download-button'),
refreshButton: document.getElementById('refresh-button'),
previewIframe: document.getElementById('preview-iframe')
};
// Quick generation buttons data
const quickGenButtons = [
{ id: 'pricing', icon: 'tag', label: 'Pricing SaaS', prompt: 'Génère une page de pricing SaaS moderne avec 3 plans (Starter, Pro, Enterprise), responsive et avec des animations' },
{ id: 'landing', icon: 'rocket', label: 'Landing Pro', prompt: 'Crée une landing page startup moderne avec hero section, features, testimonials et CTA' },
{ id: 'dashboard', icon: 'bar-chart-2', label: 'Dashboard Pro', prompt: 'Génère un dashboard analytics avec sidebar, graphiques, cards de métriques et table de données' },
{ id: 'boutique', icon: 'shopping-cart', label: 'Boutique Pro', prompt: 'Crée une boutique e-commerce avec grille de produits, filtres et panier' },
{ id: 'script', icon: 'film', label: 'Script Vidéo', prompt: 'Génère un script vidéo professionnel avec structure Hook/Body/CTA et timestamps' },
];
// Initialize the app
function init() {
renderQuickGenButtons();
setupEventListeners();
renderMessages();
updatePreview();
}
// Render quick generation buttons
function renderQuickGenButtons() {
elements.quickGenButtons.innerHTML = quickGenButtons.map(btn => `
<button
data-id="${btn.id}"
class="w-full text-left px-4 py-3 rounded-lg mb-2 transition-all hover:bg-indigo-500 bg-indigo-600/50 quick-gen-btn"
>
<i data-feather="${btn.icon}" class="mr-2"></i>
${btn.label}
</button>
`).join('');
feather.replace();
}
// Setup event listeners
function setupEventListeners() {
// Tab switching
elements.chatTab.addEventListener('click', () => switchTab('chat'));
elements.codeTab.addEventListener('click', () => switchTab('code'));
elements.jsonTab.addEventListener('click', () => switchTab('json'));
// Message sending
elements.sendButton.addEventListener('click', sendMessage);
elements.messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendMessage();
});
// Quick generation buttons
document.querySelectorAll('.quick-gen-btn').forEach(btn => {
btn.addEventListener('click', () => {
const buttonId = btn.getAttribute('data-id');
const buttonData = quickGenButtons.find(b => b.id === buttonId);
if (buttonData) {
state.activeQuickGen = buttonId;
generateCode(buttonData.prompt);
setTimeout(() => {
state.activeQuickGen = null;
// Update button styles
document.querySelectorAll('.quick-gen-btn').forEach(b => {
b.classList.remove('bg-white', 'text-indigo-600', 'shadow-lg');
b.classList.add('hover:bg-indigo-500', 'bg-indigo-600/50');
});
}, 2000);
// Update clicked button style
btn.classList.remove('hover:bg-indigo-500', 'bg-indigo-600/50');
btn.classList.add('bg-white', 'text-indigo-600', 'shadow-lg');
}
});
});
// Configuration changes
elements.themeSelect.addEventListener('change', (e) => {
state.theme = e.target.value;
});
elements.industrySelect.addEventListener('change', (e) => {
state.industry = e.target.value;
});
// Code actions
elements.copyButton.addEventListener('click', copyCode);
elements.downloadButton.addEventListener('click', downloadCode);
elements.refreshButton.addEventListener('click', updatePreview);
}
// Switch between tabs
function switchTab(tab) {
state.activeTab = tab;
// Update tab styles
elements.chatTab.classList.remove('border-indigo-600', 'text-indigo-600');
elements.codeTab.classList.remove('border-indigo-600', 'text-indigo-600');
elements.jsonTab.classList.remove('border-indigo-600', 'text-indigo-600');
elements.chatTab.classList.add('border-transparent', 'text-gray-600', 'hover:text-gray-900');
elements.codeTab.classList.add('border-transparent', 'text-gray-600', 'hover:text-gray-900');
elements.jsonTab.classList.add('border-transparent', 'text-gray-600', 'hover:text-gray-900');
// Show active tab
if (tab === 'chat') {
elements.chatTab.classList.add('border-indigo-600', 'text-indigo-600');
elements.chatTab.classList.remove('border-transparent', 'text-gray-600', 'hover:text-gray-900');
elements.chatPanel.classList.remove('hidden');
elements.codePanel.classList.add('hidden');
elements.jsonPanel.classList.add('hidden');
} else if (tab === 'code') {
elements.codeTab.classList.add('border-indigo-600', 'text-indigo-600');
elements.codeTab.classList.remove('border-transparent', 'text-gray-600', 'hover:text-gray-900');
elements.chatPanel.classList.add('hidden');
elements.codePanel.classList.remove('hidden');
elements.jsonPanel.classList.add('hidden');
} else if (tab === 'json') {
elements.jsonTab.classList.add('border-indigo-600', 'text-indigo-600');
elements.jsonTab.classList.remove('border-transparent', 'text-gray-600', 'hover:text-gray-900');
elements.chatPanel.classList.add('hidden');
elements.codePanel.classList.add('hidden');
elements.jsonPanel.classList.remove('hidden');
}
}
// Send message handler
function sendMessage() {
const message = elements.messageInput.value.trim();
if (message && !state.loading) {
generateCode(message);
elements.messageInput.value = '';
}
}
// Generate code based on prompt
function generateCode(prompt) {
state.loading = true;
state.messages.push({ role: 'user', content: prompt });
renderMessages();
// Show loading indicator
const loadingElement = document.createElement('div');
loadingElement.className = 'flex justify-start';
loadingElement.innerHTML = `
<div class="bg-gray-100 rounded-2xl px-4 py-3">
<div class="loading-dots">
<div class="loading-dot"></div>
<div class="loading-dot"></div>
<div class="loading-dot"></div>
</div>
</div>
`;
elements.messagesContainer.appendChild(loadingElement);
elements.messagesContainer.scrollTop = elements.messagesContainer.scrollHeight;
// Simulate API call with timeout
setTimeout(() => {
let html = '', css = '', js = '';
if (prompt.toLowerCase().includes('pricing')) {
html = `<div class="pricing-container">
<h1>Choisissez votre plan</h1>
<div class="pricing-grid">
<div class="pricing-card">
<h3>Starter</h3>
<div class="price">29€<span>/mois</span></div>
<ul>
<li>✓ 5 projets</li>
<li>✓ Génération illimitée</li>
<li>✓ Support email</li>
</ul>
<button class="cta-btn">Commencer</button>
</div>
<div class="pricing-card featured">
<div class="badge">Populaire</div>
<h3>Pro</h3>
<div class="price">79€<span>/mois</span></div>
<ul>
<li>✓ Projets illimités</li>
<li>✓ Génération illimitée</li>
<li>✓ Support prioritaire</li>
<li>✓ Export avancé</li>
</ul>
<button class="cta-btn">Commencer</button>
</div>
<div class="pricing-card">
<h3>Enterprise</h3>
<div class="price">199€<span>/mois</span></div>
<ul>
<li>✓ Tout de Pro</li>
<li>✓ API dédiée</li>
<li>✓ Compte manager</li>
<li>✓ SLA garanti</li>
</ul>
<button class="cta-btn">Nous contacter</button>
</div>
</div>
</div>`;
css = `* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 40px 20px; }
.pricing-container { max-width: 1200px; margin: 0 auto; }
h1 { text-align: center; color: white; font-size: 3rem; margin-bottom: 50px; }
.pricing-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 30px; }
.pricing-card { background: white; border-radius: 20px; padding: 40px; position: relative; transition: transform 0.3s ease; }
.pricing-card:hover { transform: translateY(-10px); box-shadow: 0 20px 40px rgba(0,0,0,0.2); }
.pricing-card.featured { border: 3px solid #667eea; }
.badge { position: absolute; top: -15px; right: 30px; background: #667eea; color: white; padding: 5px 20px; border-radius: 20px; font-size: 0.9rem; }
h3 { font-size: 1.8rem; color: #333; margin-bottom: 20px; }
.price { font-size: 3rem; color: #667eea; font-weight: bold; margin-bottom: 30px; }
.price span { font-size: 1.2rem; color: #999; }
ul { list-style: none; margin-bottom: 30px; }
li { padding: 10px 0; color: #666; font-size: 1.1rem; }
.cta-btn { width: 100%; padding: 15px; background: #667eea; color: white; border: none; border-radius: 10px; font-size: 1.1rem; cursor: pointer; transition: background 0.3s; }
.cta-btn:hover { background: #5568d3; }
@media (max-width: 768px) { h1 { font-size: 2rem; } .pricing-grid { grid-template-columns: 1fr; } }`;
js = `document.querySelectorAll('.cta-btn').forEach(btn => {
btn.addEventListener('click', function() {
alert('Redirection vers le paiement...');
});
});`;
} else if (prompt.toLowerCase().includes('landing')) {
html = `<div class="landing-page">
<nav class="navbar">
<div class="logo">🚀 Startup Pro</div>
<div class="nav-links">
<a href="#features">Features</a>
<a href="#pricing">Pricing</a>
<button class="nav-cta">Commencer</button>
</div>
</nav>
<section class="hero">
<h1>Créez votre succès digital</h1>
<p>La plateforme tout-en-un pour lancer votre startup en quelques clics</p>
<button class="hero-cta">Essayer gratuitement</button>
</section>
<section class="features" id="features">
<h2>Pourquoi nous choisir ?</h2>
<div class="features-grid">
<div class="feature">
<div class="icon">⚡</div>
<h3>Rapide</h3>
<p>Lancez votre projet en moins de 24h</p>
</div>
<div class="feature">
<div class="icon">🎨</div>
<h3>Design moderne</h3>
<p>Templates professionnels prêts à l'emploi</p>
</div>
<div class="feature">
<div class="icon">🔒</div>
<h3>Sécurisé</h3>
<p>Vos données protégées 24/7</p>
</div>
</div>
</section>
</div>`;
css = `* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
.navbar { display: flex; justify-content: space-between; align-items: center; padding: 20px 50px; background: white; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.logo { font-size: 1.5rem; font-weight: bold; }
.nav-links { display: flex; gap: 30px; align-items: center; }
.nav-links a { text-decoration: none; color: #333; }
.nav-cta { padding: 10px 25px; background: #4F46E5; color: white; border: none; border-radius: 8px; cursor: pointer; }
.hero { text-align: center; padding: 100px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; }
.hero h1 { font-size: 3.5rem; margin-bottom: 20px; }
.hero p { font-size: 1.3rem; margin-bottom: 40px; opacity: 0.9; }
.hero-cta { padding: 15px 40px; background: white; color: #667eea; border: none; border-radius: 10px; font-size: 1.2rem; cursor: pointer; font-weight: bold; }
.features { padding: 80px 50px; }
.features h2 { text-align: center; font-size: 2.5rem; margin-bottom: 60px; }
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 40px; max-width: 1200px; margin: 0 auto; }
.feature { text-align: center; padding: 30px; }
.icon { font-size: 4rem; margin-bottom: 20px; }
.feature h3 { font-size: 1.5rem; margin-bottom: 15px; color: #333; }
.feature p { color: #666; line-height: 1.6; }
@media (max-width: 768px) { .navbar { flex-direction: column; gap: 20px; } .hero h1 { font-size: 2rem; } }`;
js = `document.querySelector('.hero-cta').addEventListener('click', () => {
alert('Bienvenue ! Commençons votre aventure 🚀');
});`;
} else if (prompt.toLowerCase().includes('dashboard')) {
html = `<div class="dashboard">
<aside class="sidebar">
<div class="logo">📊 Dashboard</div>
<nav>
<a href="#" class="active">Vue d'ensemble</a>
<a href="#">Analytics</a>
<a href="#">Rapports</a>
<a href="#">Paramètres</a>
</nav>
</aside>
<main class="main-content">
<header>
<h1>Vue d'ensemble</h1>
<div class="user-menu">👤 Admin</div>
</header>
<div class="metrics">
<div class="metric-card">
<div class="metric-label">Revenus</div>
<div class="metric-value">45,231€</div>
<div class="metric-change positive">+20.1%</div>
</div>
<div class="metric-card">
<div class="metric-label">Utilisateurs</div>
<div class="metric-value">2,350</div>
<div class="metric-change positive">+15.3%</div>
</div>
<div class="metric-card">
<div class="metric-label">Conversions</div>
<div class="metric-value">12.5%</div>
<div class="metric-change negative">-3.2%</div>
</div>
</div>
<div class="chart-container">
<h3>Statistiques mensuelles</h3>
<div class="chart">📈 Graphique ici</div>
</div>
</main>
</div>`;
css = `* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f5f5f5; }
.dashboard { display: flex; height: 100vh; }
.sidebar { width: 250px; background: #1e293b; color: white; padding: 30px 20px; }
.logo { font-size: 1.5rem; font-weight: bold; margin-bottom: 40px; }
.sidebar nav { display: flex; flex-direction: column; gap: 10px; }
.sidebar a { color: rgba(255,255,255,0.7); text-decoration: none; padding: 12px 15px; border-radius: 8px; transition: all 0.3s; }
.sidebar a:hover, .sidebar a.active { background: #334155; color: white; }
.main-content { flex: 1; padding: 30px; overflow-y: auto; }
header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 30px; }
h1 { font-size: 2rem; color: #333; }
.user-menu { padding: 10px 20px; background: white; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
.metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 30px; }
.metric-card { background: white; padding: 25px; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.metric-label { color: #666; font-size: 0.9rem; margin-bottom: 10px; }
.metric-value { font-size: 2rem; font-weight: bold; color: #333; margin-bottom: 10px; }
.metric-change { font-size: 0.9rem; }
.positive { color: #10b981; }
.negative { color: #ef4444; }
.chart-container { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.chart { height: 300px; background: #f9fafb; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 2rem; margin-top: 20px; }`;
js = `console.log('Dashboard chargé avec succès');`;
} else if (prompt.toLowerCase().includes('boutique') || prompt.toLowerCase().includes('e-commerce')) {
html = `<div class="shop">
<nav class="shop-nav">
<div class="logo">🛍️ Ma Boutique</div>
<div class="nav-right">
<input type="search" placeholder="Rechercher..." class="search">
<div class="cart">🛒 Panier (0)</div>
</div>
</nav>
<div class="shop-container">
<aside class="filters">
<h3>Filtres</h3>
<div class="filter-group">
<h4>Catégories</h4>
<label><input type="checkbox"> Électronique</label>
<label><input type="checkbox"> Mode</label>
<label><input type="checkbox"> Maison</label>
</div>
<div class="filter-group">
<h4>Prix</h4>
<label><input type="checkbox"> 0€ - 50€</label>
<label><input type="checkbox"> 50€ - 100€</label>
<label><input type="checkbox"> 100€+</label>
</div>
</aside>
<main class="products">
<h2>Nos produits</h2>
<div class="product-grid">
<div class="product-card">
<div class="product-image">📱</div>
<h3>Smartphone Pro</h3>
<p class="price">599€</p>
<button class="add-cart">Ajouter au panier</button>
</div>
<div class="product-card">
<div class="product-image">💻</div>
<h3>Laptop Ultra</h3>
<p class="price">1299€</p>
<button class="add-cart">Ajouter au panier</button>
</div>
<div class="product-card">
<div class="product-image">🎧</div>
<h3>Casque Premium</h3>
<p class="price">249€</p>
<button class="add-cart">Ajouter au panier</button>
</div>
<div class="product-card">
<div class="product-image">⌚</div>
<h3>Smartwatch</h3>
<p class="price">399€</p>
<button class="add-cart">Ajouter au panier</button>
</div>
</div>
</main>
</div>
</div>`;
css = `* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f9fafb; }
.shop-nav { display: flex; justify-content: space-between; align-items: center; padding: 20px 40px; background: white; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.logo { font-size: 1.5rem; font-weight: bold; }
.nav-right { display: flex; gap: 20px; align-items: center; }
.search { padding: 10px 20px; border: 1px solid #ddd; border-radius: 8px; width: 300px; }
.cart { padding: 10px 20px; background: #4F46E5; color: white; border-radius: 8px; cursor: pointer; }
.shop-container { display: flex; gap: 30px; padding: 40px; max-width: 1400px; margin: 0 auto; }
.filters { width: 250px; background: white; padding: 25px; border-radius: 12px; height: fit-content; }
.filters h3 { margin-bottom: 20px; }
.filter-group { margin-bottom: 25px; }
.filter-group h4 { margin-bottom: 10px; color: #666; font-size: 0.9rem; }
.filter-group label { display: block; margin-bottom: 8px; cursor: pointer; }
.products { flex: 1; }
.products h2 { margin-bottom: 30px; font-size: 2rem; }
.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 25px; }
.product-card { background: white; border-radius: 12px; padding: 20px; text-align: center; transition: transform 0.3s; cursor: pointer; }
.product-card:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0,0,0,0.1); }
.product-image { font-size: 5rem; margin-bottom: 15px; }
.product-card h3 { font-size: 1.2rem; margin-bottom: 10px; }
.price { font-size: 1.5rem; color: #4F46E5; font-weight: bold; margin-bottom: 15px; }
.add-cart { width: 100%; padding: 12px; background: #4F46E5; color: white; border: none; border-radius: 8px; cursor: pointer; transition: background 0.3s; }
.add-cart:hover { background: #4338ca; }`;
js = `let cartCount = 0;
document.querySelectorAll('.add-cart').forEach(btn => {
btn.addEventListener('click', function() {
cartCount++;
document.querySelector('.cart').textContent = \`🛒 Panier (\${cartCount})\`;
this.textContent = '✓ Ajouté';
setTimeout(() => this.textContent = 'Ajouter au panier', 1500);
});
});`;
} else {
// Default code
html = `<div class="container">
<h1>Code généré avec succès!</h1>
<p>Votre demande: "${prompt}"</p>
<p>Utilisez les boutons rapides pour générer des templates spécifiques.</p>
</div>`;
css = `body { font-family: sans-serif; background: #f5f5f5; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; }
.container { background: white; padding: 40px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); text-align: center; max-width: 600px; }
h1 { color: #4F46E5; margin-bottom: 20px; }
p { color: #666; line-height: 1.6; }`;
js = `console.log('Code généré');`;
}
state.generatedCode = { html, css, js };
state.messages.push({
role: 'assistant',
content: `✨ Code généré avec succès ! Consultez l'onglet "Code" pour voir le résultat complet.`
});
state.loading = false;
// Update UI
renderMessages();
updateCodeDisplay();
updatePreview();
switchTab('code');
}, 1500);
}
// Render messages in chat
function renderMessages() {
elements.messagesContainer.innerHTML = state.messages.map(msg => `
<div class="flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}">
<div class="max-w-2xl rounded-2xl px-4 py-3 ${
msg.role === 'user'
? 'bg-indigo-600 text-white'
: 'bg-gray-100 text-gray-900'
}">
${msg.role === 'assistant' && msg === state.messages[0] ? `
<div class="flex items-center gap-2 mb-2">
<div class="w-8 h-8 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-white font-bold">
R
</div>
<span class="font-semibold">Rosalinda</span>
</div>
` : ''}
<div class="whitespace-pre-wrap">${msg.content}</div>
</div>
</div>
`).join('');
// Scroll to bottom
elements.messagesContainer.scrollTop = elements.messagesContainer.scrollHeight;
}
// Update code display in code panel
function updateCodeDisplay() {
elements.htmlCode.textContent = state.generatedCode.html || '// No HTML generated yet';
elements.cssCode.textContent = state.generatedCode.css || '// No CSS generated yet';
elements.jsCode.textContent = state.generatedCode.js || '// No JavaScript generated yet';
// Update JSON panel
elements.jsonOutput.textContent = JSON.stringify({
theme: state.theme,
industry: state.industry,
generated: {
html: state.generatedCode.html.length > 0,
css: state.generatedCode.css.length > 0,
js: state.generatedCode.js.length > 0
},
timestamp: new Date().toISOString()
}, null, 2);
}
// Update preview iframe
function updatePreview() {
if (elements.previewIframe && state.generatedCode.html) {
const doc = elements.previewIframe.contentDocument;
const fullCode = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>${state.generatedCode.css}</style>
</head>
<body>
${state.generatedCode.html}
<script>${state.generatedCode.js}</script>
</body>
</html>
`;
doc.open();
doc.write(fullCode);
doc.close();
}
}
// Copy code to clipboard
function copyCode() {
const fullCode = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>${state.generatedCode.css}</style>
</head>
<body>
${state.generatedCode.html}
<script>${state.generatedCode.js}</script>
</body>
</html>`;
navigator.clipboard.writeText(fullCode)
.then(() => alert('Code copié dans le presse-papier !'))
.catch(err => console.error('Failed to copy: ', err));
}
// Download code as HTML file
function downloadCode() {
const fullCode = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>${state.generatedCode.css}</style>
</head>
<body>
${state.generatedCode.html}
<script>${state.generatedCode.js}</script>
</body>
</html>`;
const blob = new Blob([fullCode], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'code-genere.html';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', init);