`;
threadContainer.insertAdjacentHTML('beforeend', tweetCard);
});
}
generateBtn.addEventListener('click', async () => {
const userTopic = userTopicEl.value.trim();
const tone = toneOfVoiceEl.value;
const count = tweetCountEl.value;
if (!userTopic) {
errorMessageEl.textContent = 'Please enter a topic or main text.';
errorMessageEl.classList.remove('hidden');
initialState.classList.add('hidden');
threadOutput.classList.add('hidden');
return;
}
// UI updates for loading state
loader.classList.remove('hidden');
initialState.classList.add('hidden');
threadOutput.classList.add('hidden');
errorMessageEl.classList.add('hidden');
generateBtn.disabled = true;
generateBtn.classList.add('opacity-60', 'cursor-not-allowed');
const prompt = `Act as a social media expert. Generate a Twitter thread of exactly ${count} tweets based on the following topic.
- Topic: "${userTopic}"
- Tone: ${tone}
- Each tweet MUST be under 280 characters.
- Ensure the tweets flow logically from one to the next.
- Respond ONLY with a single, minified JSON object with one key, "thread", which holds an array of the tweet strings. Example: {"thread":["First tweet.","Second tweet..."]}`;
try {
const thread = await generateThreadWithBackoff(prompt);
lastGeneratedThread = thread;
displayThread(thread);
threadOutput.classList.remove('hidden');
} catch (error) {
console.error('Failed to generate thread:', error);
errorMessageEl.textContent = 'Sorry, an error occurred while generating the thread. Please try again.';
errorMessageEl.classList.remove('hidden');
} finally {
loader.classList.add('hidden');
generateBtn.disabled = false;
generateBtn.classList.remove('opacity-60', 'cursor-not-allowed');
}
});
copyBtn.addEventListener('click', () => {
if (lastGeneratedThread.length === 0) return;
const threadText = lastGeneratedThread.map((tweet, i) => `(${i+1}/${lastGeneratedThread.length})\n${tweet}`).join('\n\n');
// Using the document.execCommand for broader compatibility within iframes
const textArea = document.createElement("textarea");
textArea.value = threadText;
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand('copy');
copyBtnText.textContent = 'Copied!';
setTimeout(() => { copyBtnText.textContent = 'Copy Thread'; }, 2000);
} catch (err) {
console.error('Fallback: Oops, unable to copy', err);
alert('Could not copy text.');
}
document.body.removeChild(textArea);
});
downloadPdfBtn.addEventListener('click', () => {
if (lastGeneratedThread.length === 0) return;
const { jsPDF } = window.jspdf;
document.getElementById('pdf-original-topic').textContent = userTopicEl.value;
const pdfThreadContent = document.getElementById('pdf-thread-content');
pdfThreadContent.innerHTML = '
`;
pdfThreadContent.insertAdjacentHTML('beforeend', tweetHtml);
});
const contentToPrint = document.getElementById('pdf-output');
contentToPrint.style.display = 'block';
html2canvas(contentToPrint, { scale: 2 }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const ratio = canvas.height / canvas.width;
const finalImgWidth = pdfWidth - 80; // Margins
const finalImgHeight = finalImgWidth * ratio;
pdf.addImage(imgData, 'PNG', 40, 40, finalImgWidth, finalImgHeight);
pdf.save('Twitter-Thread.pdf');
contentToPrint.style.display = 'none';
}).catch(err => {
console.error("Error generating PDF:", err);
contentToPrint.style.display = 'none';
});
});
// Initialize Lucide icons
lucide.createIcons();
});
Generated Thread
'; lastGeneratedThread.forEach((tweet, index) => { const tweetHtml = `Tweet ${index + 1} of ${lastGeneratedThread.length}
${tweet}
