blogwizard-pro / script.js
Sanjay51's picture
Customization & Flexibility
5830510 verified
// BlogWizard Pro - Main JavaScript
class BlogGenerator {
constructor() {
this.currentBlog = '';
this.currentStep = 1;
this.totalSteps = 4;
this.viewMode = 'editor'; // 'editor' or 'preview'
this.apiKeys = {};
this.outlineStructure = [];
this.initializeEventListeners();
this.loadSettings();
this.initDarkMode();
this.initMultiStepForm();
this.loadApiKeys();
}
initializeEventListeners() {
// Form submission
document.getElementById('blogForm').addEventListener('submit', (e) => {
e.preventDefault();
this.generateBlog();
});
// Blog output changes
document.getElementById('blogOutput').addEventListener('input', (e) => {
this.updateWordCount();
this.currentBlog = e.target.value;
this.updatePreview();
});
// Model change
document.getElementById('model').addEventListener('change', (e) => {
localStorage.setItem('selectedModel', e.target.value);
});
}
initMultiStepForm() {
this.updateStepDisplay();
}
initDarkMode() {
const html = document.documentElement;
// Check for saved preference
const darkMode = localStorage.getItem('darkMode') === 'true';
if (darkMode) {
html.classList.add('dark');
}
}
loadSettings() {
const savedModel = localStorage.getItem('selectedModel');
const savedProvider = localStorage.getItem('aiProvider');
if (savedModel) {
document.getElementById('model').value = savedModel;
}
// Update placeholder based on selected model
this.updateApiKeyPlaceholder(document.getElementById('model').value);
// Add model change listener to update placeholder
document.getElementById('model').addEventListener('change', (e) => {
this.updateApiKeyPlaceholder(e.target.value);
this.autoSwitchApiKey(e.target.value);
});
}
loadApiKeys() {
const savedKeys = localStorage.getItem('apiKeys');
if (savedKeys) {
this.apiKeys = JSON.parse(savedKeys);
this.displaySavedKeys();
}
}
displaySavedKeys() {
const savedKeysDiv = document.getElementById('savedKeys');
if (Object.keys(this.apiKeys).length === 0) {
savedKeysDiv.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400">No saved API keys yet</p>';
return;
}
savedKeysDiv.innerHTML = Object.entries(this.apiKeys).map(([model, key]) => `
<div class="flex items-center justify-between p-2 bg-gray-50 dark:bg-gray-700 rounded">
<span class="text-sm text-gray-700 dark:text-gray-300">
${model.replace('-', ' ').toUpperCase()}: ${key.substring(0, 10)}...
</span>
<button onclick="useSavedKey('${model}')" class="text-primary hover:underline text-sm">
Use
</button>
</div>
`).join('');
}
saveApiKeyModel(model, key) {
this.apiKeys[model] = key;
localStorage.setItem('apiKeys', JSON.stringify(this.apiKeys));
this.displaySavedKeys();
this.showToast(`API key saved for ${model}`, 'success');
}
autoSwitchApiKey(model) {
if (this.apiKeys[model]) {
document.getElementById('apiKey').value = this.apiKeys[model];
this.validateApiKey(this.apiKeys[model]);
}
}
updateStepDisplay() {
// Hide all steps
for (let i = 1; i <= this.totalSteps; i++) {
document.getElementById(`step${i}`).classList.add('hidden');
document.getElementById(`step${i}Indicator`).classList.remove('active');
}
// Show current step
document.getElementById(`step${this.currentStep}`).classList.remove('hidden');
document.getElementById(`step${this.currentStep}Indicator`).classList.add('active');
// Update navigation buttons
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const generateBtn = document.getElementById('generateBtn');
if (this.currentStep === 1) {
prevBtn.classList.add('hidden');
nextBtn.classList.remove('hidden');
generateBtn.classList.add('hidden');
} else if (this.currentStep === this.totalSteps) {
prevBtn.classList.remove('hidden');
nextBtn.classList.add('hidden');
generateBtn.classList.remove('hidden');
this.updateSummary();
} else {
prevBtn.classList.remove('hidden');
nextBtn.classList.remove('hidden');
generateBtn.classList.add('hidden');
}
}
updateSummary() {
const topic = document.getElementById('topic').value;
const keywords = document.getElementById('keywords').value;
const tone = document.getElementById('tone').value;
const length = document.getElementById('length').value;
const language = document.getElementById('language').value;
const model = document.getElementById('model').value;
const summary = document.getElementById('summary');
summary.innerHTML = `
<div class="flex justify-between">
<span class="font-medium">Topic:</span>
<span>${topic || 'Not specified'}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Keywords:</span>
<span>${keywords || 'None'}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Tone:</span>
<span>${tone.charAt(0).toUpperCase() + tone.slice(1)}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Length:</span>
<span>${length.charAt(0).toUpperCase() + length.slice(1)}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Language:</span>
<span>${this.getLanguageName(language)}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Model:</span>
<span>${model.replace('-', ' ').toUpperCase()}</span>
</div>
`;
}
getLanguageName(code) {
const languages = {
'en': 'English',
'es': 'Spanish',
'fr': 'French',
'de': 'German',
'zh': 'Chinese'
};
return languages[code] || code;
}
updateApiKeyPlaceholder(model) {
const apiKeyInput = document.getElementById('apiKey');
if (model.startsWith('gemini')) {
apiKeyInput.placeholder = 'Enter Gemini API key...';
} else if (model.startsWith('claude')) {
apiKeyInput.placeholder = 'sk-ant-...';
} else {
apiKeyInput.placeholder = 'sk-...';
}
}
async generateBlog() {
const topic = document.getElementById('topic').value;
const keywords = document.getElementById('keywords').value;
const tone = document.getElementById('tone').value;
const length = document.getElementById('length').value;
const language = document.getElementById('language').value;
const apiKey = document.getElementById('apiKey').value;
const model = document.getElementById('model').value;
if (!apiKey) {
this.showToast('Please enter your API key in settings', 'error');
return;
}
// Show loading state
const outputSection = document.getElementById('outputSection');
const loadingSpinner = document.getElementById('loadingSpinner');
const blogOutput = document.getElementById('blogOutput');
outputSection.classList.remove('hidden');
outputSection.classList.add('slide-in');
loadingSpinner.classList.remove('hidden');
blogOutput.value = '';
// Create prompt
const prompt = this.createPrompt(topic, keywords, tone, length, language);
try {
// Simulate API call (replace with actual API call)
const generatedContent = await this.callAI(prompt, apiKey, model);
setTimeout(() => {
loadingSpinner.classList.add('hidden');
blogOutput.value = generatedContent;
this.currentBlog = generatedContent;
this.updateWordCount();
this.updateTimestamp();
this.showToast('Blog generated successfully!', 'success');
}, 2000);
} catch (error) {
loadingSpinner.classList.add('hidden');
this.showToast('Error generating blog: ' + error.message, 'error');
}
}
createPrompt(topic, keywords, tone, length, language) {
const wordCounts = {
short: '300-500',
medium: '500-800',
long: '800-1200'
};
return `Write a blog post about "${topic}".
Keywords to include: ${keywords}.
Tone: ${tone}.
Length: ${wordCounts[length]} words.
Language: ${language}.
Make it engaging and well-structured with an introduction, body paragraphs, and conclusion.
Include a catchy title.`;
}
async callAI(prompt, apiKey, model) {
// Check if it's a Gemini model
if (model.startsWith('gemini')) {
return await this.callGemini(prompt, apiKey, model);
} else if (model.startsWith('gpt')) {
return await this.callOpenAI(prompt, apiKey, model);
} else if (model.startsWith('claude')) {
return await this.callClaude(prompt, apiKey, model);
}
// Fallback to Gemini if model not recognized
return await this.callGemini(prompt, apiKey, 'gemini-pro');
}
async callOpenAI(prompt, apiKey, model) {
try {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model: model,
messages: [{ role: 'user', content: prompt }],
max_tokens: 2000,
temperature: 0.7
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.choices[0].message.content;
} catch (error) {
if (error.message.includes('API key')) {
throw new Error('Invalid OpenAI API key. Please check your API key and try again.');
} else if (error.message.includes('quota')) {
throw new Error('OpenAI API quota exceeded. Please check your usage and billing.');
} else {
throw new Error(`OpenAI API error: ${error.message}`);
}
}
}
async callClaude(prompt, apiKey, model) {
try {
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: model,
max_tokens: 2000,
messages: [{ role: 'user', content: prompt }]
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.content[0].text;
} catch (error) {
if (error.message.includes('API key')) {
throw new Error('Invalid Claude API key. Please check your API key and try again.');
} else if (error.message.includes('quota')) {
throw new Error('Claude API quota exceeded. Please check your usage and billing.');
} else {
throw new Error(`Claude API error: ${error.message}`);
}
}
}
async callGemini(prompt, apiKey, model) {
try {
const response = await fetch(`https://generativelanguage.googleapis.com/v1/models/${model}:generateContent?key=${apiKey}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
contents: [{
parts: [{
text: prompt
}]
}]
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.candidates[0].content.parts[0].text;
} catch (error) {
if (error.message.includes('API key')) {
throw new Error('Invalid Gemini API key. Please check your API key and try again.');
} else if (error.message.includes('quota')) {
throw new Error('Gemini API quota exceeded. Please check your usage and billing.');
} else {
throw new Error(`Gemini API error: ${error.message}`);
}
}
}
updateWordCount() {
const blogOutput = document.getElementById('blogOutput');
const text = blogOutput.value;
const wordCount = text.trim() ? text.trim().split(/\s+/).length : 0;
const readingTime = Math.max(1, Math.ceil(wordCount / 200)); // Average reading speed: 200 words/min
document.getElementById('wordCount').textContent = `${wordCount} words`;
document.getElementById('readingTime').textContent = `${readingTime} min read`;
// Calculate readability score (simplified Flesch Reading Ease)
if (text) {
const sentences = text.split(/[.!?]+/).length;
const avgWordsPerSentence = wordCount / sentences;
const avgSyllables = this.countSyllables(text) / wordCount;
const score = 206.835 - (1.015 * avgWordsPerSentence) - (84.6 * avgSyllables);
let readability = Math.round(score);
if (readability > 100) readability = 100;
if (readability < 0) readability = 0;
let level = 'Very Difficult';
if (readability >= 90) level = 'Very Easy';
else if (readability >= 80) level = 'Easy';
else if (readability >= 70) level = 'Fairly Easy';
else if (readability >= 60) level = 'Standard';
else if (readability >= 50) level = 'Fairly Difficult';
else if (readability >= 30) level = 'Difficult';
document.getElementById('readabilityScore').textContent = `Score: ${readability} (${level})`;
} else {
document.getElementById('readabilityScore').textContent = 'Score: --';
}
}
countSyllables(text) {
const words = text.toLowerCase().split(/\s+/);
let syllableCount = 0;
words.forEach(word => {
word = word.replace(/[^a-z]/g, '');
if (word.length === 0) return;
let count = 0;
let prevCharWasVowel = false;
const vowels = 'aeiouy';
for (let i = 0; i < word.length; i++) {
const isVowel = vowels.includes(word[i]);
if (isVowel && !prevCharWasVowel) {
count++;
}
prevCharWasVowel = isVowel;
}
if (word.endsWith('e')) count--;
if (count === 0) count = 1;
syllableCount += count;
});
return syllableCount;
}
updateTimestamp() {
const now = new Date();
const timestamp = now.toLocaleString();
document.getElementById('timestamp').textContent = `Generated on ${timestamp}`;
}
regenerateBlog() {
this.generateBlog();
}
copyBlog() {
const blogOutput = document.getElementById('blogOutput');
blogOutput.select();
document.execCommand('copy');
this.showToast('Blog copied to clipboard!', 'success');
}
downloadBlog(format) {
const blogOutput = document.getElementById('blogOutput');
const topic = document.getElementById('topic').value || 'blog';
const timestamp = new Date().toISOString().slice(0, 10);
const filename = `${topic}-${timestamp}.${format}`;
let content = blogOutput.value;
if (format === 'md' && !content.startsWith('#')) {
// Add markdown title if not present
content = `# ${topic}\n\n${content}`;
}
const blob = new Blob([content], { type: format === 'md' ? 'text/markdown' : 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
this.showToast(`Blog downloaded as ${format.toUpperCase()}!`, 'success');
}
formatText(formatType) {
const textarea = document.getElementById('blogOutput');
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const selectedText = textarea.value.substring(start, end);
let formattedText = '';
switch(formatType) {
case 'bold':
formattedText = `**${selectedText}**`;
break;
case 'italic':
formattedText = `*${selectedText}*`;
break;
case 'heading':
formattedText = selectedText ? `## ${selectedText}` : '## ';
break;
case 'list':
formattedText = selectedText ? `- ${selectedText}` : '- ';
break;
case 'link':
const url = prompt('Enter URL:', 'https://');
formattedText = url ? `[${selectedText || 'link text'}](${url})` : selectedText;
break;
case 'quote':
formattedText = selectedText ? `> ${selectedText}` : '> ';
break;
case 'code':
formattedText = selectedText ? `\`${selectedText}\`` : '``';
break;
default:
formattedText = selectedText;
}
textarea.value = textarea.value.substring(0, start) + formattedText + textarea.value.substring(end);
textarea.selectionStart = textarea.selectionEnd = start + formattedText.length;
textarea.focus();
this.currentBlog = textarea.value;
this.updateWordCount();
this.updatePreview();
}
toggleViewMode() {
const editorView = document.getElementById('editorView');
const previewView = document.getElementById('previewView');
const viewModeText = document.getElementById('viewModeText');
const editorToolbar = document.getElementById('editorToolbar');
if (this.viewMode === 'editor') {
this.viewMode = 'preview';
editorView.classList.add('hidden');
previewView.classList.remove('hidden');
editorToolbar.classList.add('hidden');
viewModeText.textContent = 'Edit';
this.updatePreview();
} else {
this.viewMode = 'editor';
editorView.classList.remove('hidden');
previewView.classList.add('hidden');
editorToolbar.classList.remove('hidden');
viewModeText.textContent = 'Preview';
}
}
updatePreview() {
const content = document.getElementById('blogOutput').value;
const preview = document.getElementById('blogPreview');
// Simple markdown to HTML conversion
let html = content
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
.replace(/^\* (.+)$/gm, '<li>$1</li>')
.replace(/^- (.+)$/gm, '<li>$1</li>')
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.+?)\*/g, '<em>$1</em>')
.replace(/`(.+?)`/g, '<code>$1</code>')
.replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>')
.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2">$1</a>')
.replace(/\n\n/g, '</p><p>')
.replace(/\n/g, '<br>');
// Wrap in paragraphs if no HTML tags exist
if (!html.includes('<')) {
html = `<p>${html}</p>`;
}
preview.innerHTML = html;
}
showTooltip(field) {
const tooltips = {
'topic': 'Enter the main topic or title for your blog post. Be specific for better results.',
'keywords': 'Add relevant keywords to include in your blog. Separate them with commas.',
'tone': 'Choose the writing style: Professional for formal content, Casual for friendly tone, Humorous for entertaining content, Inspirational for motivation, or Technical for detailed explanations.',
'length': 'Select the approximate word count: Short (300-500 words), Medium (500-800 words), or Long (800-1200 words).',
'language': 'Choose the language for your blog post.',
'model': 'Select the AI model to generate your content. Each has unique strengths and capabilities.',
'apiKey': 'Enter your API key for the selected AI model. Your key is stored locally and never shared.'
};
const tooltip = document.getElementById('tooltip');
const tooltipContent = document.getElementById('tooltipContent');
const button = event.target.closest('button');
const rect = button.getBoundingClientRect();
tooltipContent.textContent = tooltips[field];
tooltip.style.top = `${rect.bottom + 10}px`;
tooltip.style.left = `${rect.left + rect.width / 2}px`;
tooltip.style.transform = 'translateX(-50%)';
tooltip.classList.remove('hidden');
// Hide tooltip when clicking outside
setTimeout(() => {
document.addEventListener('click', function hideTooltip(e) {
if (!tooltip.contains(e.target) && !button.contains(e.target)) {
tooltip.classList.add('hidden');
document.removeEventListener('click', hideTooltip);
}
});
}, 100);
}
validateCurrentStep() {
switch(this.currentStep) {
case 1:
const topic = document.getElementById('topic').value.trim();
if (!topic) {
this.showToast('Please enter a blog topic', 'error');
return false;
}
break;
case 3:
const apiKey = document.getElementById('apiKey').value.trim();
if (!apiKey) {
this.showToast('Please enter your API key', 'error');
return false;
}
break;
}
return true;
}
showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = 'toast';
toast.innerHTML = `
<div class="flex items-center">
<i data-feather="${type === 'success' ? 'check-circle' : type === 'error' ? 'x-circle' : 'info'}"
class="mr-2 ${type === 'success' ? 'text-green-500' : type === 'error' ? 'text-red-500' : 'text-blue-500'}"></i>
<span>${message}</span>
</div>
`;
document.body.appendChild(toast);
feather.replace();
setTimeout(() => toast.classList.add('show'), 100);
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
async suggestStructure() {
const topic = document.getElementById('topic').value;
const keywords = document.getElementById('keywords').value;
const length = document.getElementById('length').value;
if (!topic) {
this.showToast('Please enter a topic first', 'error');
return;
}
const sections = {
short: ['Introduction', 'Main Point', 'Conclusion'],
medium: ['Introduction', 'Point 1', 'Point 2', 'Point 3', 'Conclusion'],
long: ['Introduction', 'Background', 'Key Concepts', 'Detailed Analysis', 'Case Studies', 'Best Practices', 'Future Outlook', 'Conclusion']
};
this.outlineStructure = sections[length] || sections.medium;
const outlineContent = document.getElementById('outlineContent');
outlineContent.innerHTML = this.outlineStructure.map((section, index) => `
<div class="flex items-center gap-2 p-2 bg-white dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-600">
<span class="text-gray-400">${index + 1}.</span>
<input type="text" value="${section}"
class="flex-1 px-2 py-1 border-0 bg-transparent text-gray-700 dark:text-gray-300 focus:outline-none focus:ring-1 focus:ring-primary"
onchange="updateOutlineSection(${index}, this.value)">
<button onclick="removeOutlineSection(${index})" class="text-red-500 hover:text-red-700">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
</div>
`).join('');
outlineContent.classList.remove('hidden');
feather.replace();
this.showToast('Structure suggested! You can edit sections above.', 'success');
}
updateOutlineSection(index, value) {
this.outlineStructure[index] = value;
}
removeOutlineSection(index) {
this.outlineStructure.splice(index, 1);
this.suggestStructure(); // Refresh the outline
}
}
// Initialize the app
let blogGenerator;
document.addEventListener('DOMContentLoaded', () => {
blogGenerator = new BlogGenerator();
});
// Global functions for onclick handlers
function saveApiKey() {
const apiKey = document.getElementById('apiKey').value;
const model = document.getElementById('model').value;
if (apiKey && model) {
blogGenerator.saveApiKeyModel(model, apiKey);
} else {
blogGenerator.showToast('Please enter an API key', 'error');
}
}
function useSavedKey(model) {
document.getElementById('model').value = model;
document.getElementById('apiKey').value = blogGenerator.apiKeys[model];
blogGenerator.validateApiKey(blogGenerator.apiKeys[model]);
blogGenerator.showToast('API key loaded', 'success');
}
function validateApiKey(key) {
const statusSpan = document.getElementById('keyStatus');
if (!key) {
statusSpan.innerHTML = '';
return;
}
let isValid = false;
let provider = '';
if (key.startsWith('sk-ant-')) {
isValid = key.length > 20;
provider = 'Claude';
} else if (key.startsWith('sk-')) {
isValid = key.length > 20;
provider = 'OpenAI';
} else if (key.length > 20) {
isValid = true;
provider = 'Gemini';
}
if (isValid) {
statusSpan.innerHTML = `<span class="text-green-500">✓ Valid ${provider} key</span>`;
} else {
statusSpan.innerHTML = `<span class="text-red-500">✗ Invalid format</span>`;
}
}
function handleToneChange(value) {
const customToneField = document.getElementById('customToneField');
if (value === 'custom') {
customToneField.classList.remove('hidden');
} else {
customToneField.classList.add('hidden');
}
}
function handleImageUpload(event) {
const file = event.target.files[0];
if (file && file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = function(e) {
const previewDiv = document.getElementById('uploadedImagePreview');
const previewImg = document.getElementById('previewImg');
previewImg.src = e.target.result;
previewDiv.classList.remove('hidden');
};
reader.readAsDataURL(file);
}
}
async function suggestStockImages() {
const topic = document.getElementById('topic').value;
const keywords = document.getElementById('keywords').value;
if (!topic) {
blogGenerator.showToast('Please enter a topic first', 'error');
return;
}
const suggestionsDiv = document.getElementById('stockImageSuggestions');
suggestionsDiv.innerHTML = '<div class="col-span-full text-center">Loading image suggestions...</div>';
suggestionsDiv.classList.remove('hidden');
// Generate 4 stock image suggestions using static.photos
const imageUrls = [
`http://static.photos/technology/320x240/1`,
`http://static.photos/office/320x240/2`,
`http://static.photos/workspace/320x240/3`,
`http://static.photos/abstract/320x240/4`
];
setTimeout(() => {
suggestionsDiv.innerHTML = imageUrls.map((url, index) => `
<div class="relative group cursor-pointer" onclick="selectStockImage('${url}')">
<img src="${url}" alt="Suggestion ${index + 1}" class="w-full h-32 object-cover rounded-lg">
<div class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-30 transition-all duration-200 rounded-lg flex items-center justify-center">
<i data-feather="check-circle" class="w-8 h-8 text-white opacity-0 group-hover:opacity-100 transition-all duration-200"></i>
</div>
</div>
`).join('');
feather.replace();
}, 1000);
}
function selectStockImage(url) {
document.getElementById('imageUrl').value = url;
blogGenerator.showToast('Image selected!', 'success');
}
function showApiGuide() {
document.getElementById('apiGuideModal').classList.remove('hidden');
feather.replace();
}
function closeApiGuide() {
document.getElementById('apiGuideModal').classList.add('hidden');
}
function updateOutlineSection(index, value) {
blogGenerator.updateOutlineSection(index, value);
}
function removeOutlineSection(index) {
blogGenerator.removeOutlineSection(index);
}
function fillTemplate(type) {
const templates = {
marketing: {
topic: 'The Future of AI in Marketing',
keywords: 'AI, marketing, automation, future',
tone: 'informative',
length: 'medium',
language: 'en'
},
security: {
topic: 'Web Security Best Practices for Developers',
keywords: 'security, web development, best practices',
tone: 'technical',
length: 'long',
language: 'en'
},
productivity: {
topic: 'Top Productivity Tools for Solopreneurs',
keywords: 'productivity, tools, solopreneur, business',
tone: 'casual',
length: 'short',
language: 'en'
}
};
const template = templates[type];
Object.keys(template).forEach(key => {
const element = document.getElementById(key);
if (element) {
element.value = template[key];
}
});
blogGenerator.showToast('Template applied!', 'success');
}
function regenerateBlog() {
blogGenerator.regenerateBlog();
}
function copyBlog() {
blogGenerator.copyBlog();
}
function downloadBlog(format) {
blogGenerator.downloadBlog(format);
}
function toggleDarkMode() {
const html = document.documentElement;
const darkMode = !html.classList.contains('dark');
if (darkMode) {
html.classList.add('dark');
} else {
html.classList.remove('dark');
}
localStorage.setItem('darkMode', darkMode);
}
function changeStep(direction) {
blogGenerator.currentStep += direction;
// Validate current step before moving forward
if (direction > 0) {
if (!blogGenerator.validateCurrentStep()) {
return;
}
}
blogGenerator.currentStep = Math.max(1, Math.min(blogGenerator.totalSteps, blogGenerator.currentStep));
blogGenerator.updateStepDisplay();
}
function openHelpModal() {
document.getElementById('helpModal').classList.remove('hidden');
feather.replace();
}
function closeHelpModal() {
document.getElementById('helpModal').classList.add('hidden');
}
function showTooltip(field) {
blogGenerator.showTooltip(field);
}
function toggleViewMode() {
blogGenerator.toggleViewMode();
}
function formatText(formatType) {
blogGenerator.formatText(formatType);
}