document.addEventListener('DOMContentLoaded', function() { const markdownInput = document.getElementById('markdown-input'); const htmlOutput = document.getElementById('html-output'); const selectAllBtn = document.getElementById('select-all'); const copyBtn = document.getElementById('copy'); function updateOutput() { const markdown = markdownInput.value; if (!markdown.trim()) { htmlOutput.innerHTML = ''; return; } // Convert markdown to HTML client-side const html = marked(markdown); htmlOutput.innerHTML = html; // Apply alternating column colors to tables const tables = htmlOutput.querySelectorAll('table'); tables.forEach(table => { const rows = table.querySelectorAll('tr'); rows.forEach(row => { const cells = row.querySelectorAll('td'); cells.forEach((cell, index) => { if (index % 2 === 0) { cell.style.backgroundColor = 'rgba(0, 0, 0, 0.25)'; } }); }); }); } markdownInput.addEventListener('input', updateOutput); selectAllBtn.addEventListener('click', function() { if (!htmlOutput.innerHTML.trim()) { alert('No content to select'); return; } const range = document.createRange(); range.selectNodeContents(htmlOutput); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); }); copyBtn.addEventListener('click', function() { if (!htmlOutput.innerHTML.trim()) { alert('No content to copy'); return; } // Use modern clipboard API if available if (navigator.clipboard && window.isSecureContext) { // Copy both HTML and plain text formats const htmlContent = htmlOutput.innerHTML; const textContent = htmlOutput.textContent || htmlOutput.innerText; const clipboardItem = new ClipboardItem({ 'text/html': new Blob([htmlContent], { type: 'text/html' }), 'text/plain': new Blob([textContent], { type: 'text/plain' }) }); navigator.clipboard.write([clipboardItem]).then(() => { showNotification('Rich text copied to clipboard!'); }).catch(err => { console.error('Failed to copy rich text:', err); // Fallback to plain text if rich text copying fails navigator.clipboard.writeText(textContent).then(() => { showNotification('Text copied to clipboard!'); }).catch(() => { fallbackCopy(); }); }); } else { fallbackCopy(); } }); function fallbackCopy() { const range = document.createRange(); range.selectNodeContents(htmlOutput); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); try { document.execCommand('copy'); showNotification('Rich text copied to clipboard!'); } catch (err) { console.error('Fallback copy failed:', err); showNotification('Failed to copy to clipboard', 'error'); } selection.removeAllRanges(); } function showNotification(message, type = 'success') { // Create notification element const notification = document.createElement('div'); notification.textContent = message; // Set styles individually to avoid template literal issues notification.style.position = 'fixed'; notification.style.top = '20px'; notification.style.right = '20px'; notification.style.background = type === 'error' ? '#f44336' : '#4caf50'; notification.style.color = 'white'; notification.style.padding = '12px 20px'; notification.style.borderRadius = '4px'; notification.style.fontFamily = "'Montserrat', Arial, sans-serif"; notification.style.zIndex = '1000'; notification.style.animation = 'slideIn 0.3s ease'; // Add CSS animation const style = document.createElement('style'); style.textContent = '@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }'; document.head.appendChild(style); document.body.appendChild(notification); // Remove notification after 3 seconds setTimeout(() => { notification.remove(); style.remove(); }, 3000); } // Handle authentication errors gracefully window.addEventListener('unhandledrejection', function(event) { if (event.reason && event.reason.message && event.reason.message.includes('authentication')) { showNotification('Authentication error. Please refresh and login again.', 'error'); } }); });