SEO Meta Description Generator
Craft compelling, keyword-rich meta descriptions to boost your click-through rates.
Configuration
Your generated meta descriptions will appear here.
Failed to generate descriptions. Please check your input or try again. Error: ${error.message}
`; } finally { showLoadingState(false); } } function renderResults(descriptions) { elements.resultsContainer.innerHTML = ''; if (Array.isArray(descriptions) && descriptions.length > 0) { descriptions.forEach((desc, index) => { const charCount = desc.length; const card = document.createElement('div'); card.className = 'result-card'; card.innerHTML = `${desc}
${charCount} Characters
`;
elements.resultsContainer.appendChild(card);
});
elements.pdfDownloadSection.classList.remove('hidden');
} else {
elements.resultsContainer.innerHTML = `The AI did not return any valid descriptions. Please try refining your input.
`; } } window.copyToClipboard = (button, index) => { const textArea = document.getElementById(`desc-text-${index}`); if (textArea) { textArea.select(); // Using execCommand for iframe compatibility as per spec document.execCommand('copy'); button.innerHTML = `Copied!`; setTimeout(() => { button.innerHTML = `Copy`; }, 2000); } } async function callGeminiApi(systemPrompt, userQuery, retries = 3, delay = 1000) { const apiKey = ""; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${apiKey}`; const payload = { contents: [{ parts: [{ text: userQuery }] }], systemInstruction: { parts: [{ text: systemPrompt }] }, generationConfig: { responseMimeType: "application/json" } }; for (let i = 0; i < retries; i++) { try { const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) throw new Error(`API request failed with status ${response.status}`); const result = await response.json(); const candidate = result.candidates?.[0]; if (candidate?.content?.parts?.[0]?.text) { return candidate.content.parts[0].text; } else { throw new Error(`API returned no content. Reason: ${candidate?.finishReason || 'Unknown'}`); } } catch (error) { console.error(`Attempt ${i + 1} failed:`, error.message); if (i < retries - 1) await new Promise(res => setTimeout(res, delay *= 2)); else throw error; } } } function showLoadingState(isLoading) { elements.resultsPlaceholder.classList.add('hidden'); if (isLoading) { elements.resultsContainer.innerHTML = ''; elements.generateBtn.disabled = true; elements.generateBtn.innerHTML = `Generating...`; elements.pdfDownloadSection.classList.add('hidden'); } else { elements.generateBtn.disabled = false; elements.generateBtn.innerHTML = `Generate Descriptions`; } } function generatePdf() { const topic = elements.pageTopic.value; const keywords = elements.targetKeywords.value; const tone = elements.toneOfVoice.value; const descriptions = Array.from(elements.resultsContainer.querySelectorAll('.result-card p')).map(p => p.textContent); if (descriptions.length === 0) { alert('Please generate descriptions before downloading.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); doc.setFont('helvetica', 'bold'); doc.setFontSize(18); doc.setTextColor('#1f2937'); doc.text('SEO Meta Description Report', doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' }); // Input summary table doc.autoTable({ startY: 30, head: [['Configuration', 'Details']], body: [ ['Topic / Title', topic], ['Keywords', keywords], ['Tone of Voice', tone] ], theme: 'striped', headStyles: { fillColor: '#4f46e5' }, }); // Generated descriptions const descriptionBody = descriptions.map((desc, index) => [`${index + 1}`, desc, `${desc.length}`]); doc.autoTable({ startY: doc.autoTable.previous.finalY + 15, head: [['#', 'Generated Description', 'Chars']], body: descriptionBody, theme: 'grid', headStyles: { fillColor: '#059669' }, }); doc.save('seo-meta-descriptions.pdf'); } });