Online Lecture & Meeting Notes Highlighter

Online Lecture & Meeting Notes Highlighter

Step 1: Paste Your Notes

${line || ' '}

`).join(''); // Switch views inputArea.classList.add('hidden'); highlightingArea.classList.remove('hidden'); }; const applyHighlight = (color) => { const selection = window.getSelection(); if (!selection.rangeCount || selection.isCollapsed) return; const range = selection.getRangeAt(0); const selectionParent = range.commonAncestorContainer.nodeType === 3 ? range.commonAncestorContainer.parentNode : range.commonAncestorContainer; // Ensure selection is within the editor if (!notesEditor.contains(selectionParent)) { return; } // Create a span for the highlight const highlightSpan = document.createElement('span'); highlightSpan.className = `highlight-${color}`; try { // This is a robust way to wrap the selected content, even if it spans multiple nodes. range.surroundContents(highlightSpan); } catch (e) { // Fallback for complex selections (e.g., across paragraphs) // This is a simplified approach. A production tool would need a more complex algorithm. console.warn("Complex selection not fully supported for highlighting. Applying to fragment.", e); highlightSpan.appendChild(range.extractContents()); range.insertNode(highlightSpan); } selection.removeAllRanges(); }; const clearHighlight = () => { const selection = window.getSelection(); if (!selection.rangeCount || selection.isCollapsed) return; const range = selection.getRangeAt(0); let selectedElement = range.commonAncestorContainer; // Traverse up to find a highlight span if text node is selected if (selectedElement.nodeType === 3) { selectedElement = selectedElement.parentNode; } // If the selected element (or its parent) is a highlight, unwrap it if (selectedElement.tagName === 'SPAN' && selectedElement.className.startsWith('highlight-')) { const parent = selectedElement.parentNode; while (selectedElement.firstChild) { parent.insertBefore(selectedElement.firstChild, selectedElement); } parent.removeChild(selectedElement); // Normalize to merge adjacent text nodes parent.normalize(); } }; const downloadPdf = () => { if (typeof window.jspdf === 'undefined') { console.error("jsPDF library is not loaded."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); doc.setFontSize(18); doc.text('Annotated Notes', 14, 22); doc.setFontSize(11); doc.setTextColor(100); doc.text(`Exported on: ${new Date().toLocaleString('en-US')}`, 14, 30); doc.setLineWidth(0.5); doc.line(14, 34, 196, 34); let yPosition = 45; const pageHeight = doc.internal.pageSize.height; const margin = 14; const processNode = (node) => { if (node.nodeType === Node.TEXT_NODE) { return [{ text: node.textContent, color: null }]; } if (node.nodeType === Node.ELEMENT_NODE) { const style = node.className; let color = null; if (style.includes('highlight-yellow')) color = '#FBBF24'; // amber-400 if (style.includes('highlight-blue')) color = '#60A5FA'; // blue-400 if (style.includes('highlight-green')) color = '#34D399'; // green-400 if (style.includes('highlight-pink')) color = '#F472B6'; // pink-400 let parts = []; node.childNodes.forEach(child => { const childParts = processNode(child); childParts.forEach(part => { // Inherit color if child doesn't have its own parts.push({ text: part.text, color: part.color || color }); }); }); return parts; } return []; }; Array.from(notesEditor.children).forEach(p => { if (yPosition > pageHeight - 20) { doc.addPage(); yPosition = 20; } const textParts = processNode(p); const lines = doc.splitTextToSize(textParts.map(part => part.text).join(''), 180); // This is a simplified rendering. A more advanced version would handle // highlights spanning across lines. let currentX = margin; textParts.forEach(part => { if (part.color) { doc.setFillColor(part.color); doc.rect(currentX - 1, yPosition - 4, doc.getStringUnitWidth(part.text) * doc.getFontSize() / doc.internal.scaleFactor + 2, 6, 'F'); } doc.text(part.text, currentX, yPosition); currentX += doc.getStringUnitWidth(part.text) * doc.getFontSize() / doc.internal.scaleFactor; }); yPosition += (lines.length * 7); // Move y down for the next paragraph }); doc.save('annotated-notes.pdf'); }; // --- EVENT LISTENERS --- processTextBtn?.addEventListener('click', processAndDisplayNotes); highlightBtns.forEach(btn => { btn.addEventListener('click', () => { const color = btn.getAttribute('data-color'); applyHighlight(color); }); }); clearHighlightBtn?.addEventListener('click', clearHighlight); downloadPdfBtn?.addEventListener('click', downloadPdf); });
Scroll to Top