class MarkdownExtraction { constructor() { this.selectedElements = new Set(); this.highlightBoxes = new Map(); this.selectionMode = false; this.toolbar = null; this.markdownPreviewModal = null; this.selectionCounter = 0; this.markdownConverter = null; this.contentAnalyzer = null; this.init(); } async init() { // Initialize dependencies this.markdownConverter = new MarkdownConverter(); this.contentAnalyzer = new ContentAnalyzer(); this.createToolbar(); this.setupEventListeners(); } createToolbar() { // Create floating toolbar this.toolbar = document.createElement('div'); this.toolbar.className = 'c4ai-c2c-toolbar'; this.toolbar.innerHTML = `
`; document.body.appendChild(this.toolbar); makeDraggableByHeader(this.toolbar); // Position toolbar this.toolbar.style.position = 'fixed'; this.toolbar.style.top = '20px'; this.toolbar.style.right = '20px'; this.toolbar.style.zIndex = '999999'; } setupEventListeners() { // Close button this.toolbar.querySelector('.c4ai-close-btn').addEventListener('click', () => { this.deactivate(); }); // Clear selection button this.toolbar.querySelector('.c4ai-clear-btn').addEventListener('click', () => { this.clearSelection(); }); // Preview button this.toolbar.querySelector('.c4ai-preview-btn').addEventListener('click', () => { this.showPreview(); }); // Copy button this.toolbar.querySelector('.c4ai-copy-btn').addEventListener('click', () => { this.copyToClipboard(); }); // Document click handler for element selection this.documentClickHandler = (event) => this.handleElementClick(event); document.addEventListener('click', this.documentClickHandler, true); // Prevent default link behavior during selection mode this.linkClickHandler = (event) => { if (event.ctrlKey || event.metaKey) { event.preventDefault(); event.stopPropagation(); } }; document.addEventListener('click', this.linkClickHandler, true); // Hover effect this.documentHoverHandler = (event) => this.handleElementHover(event); document.addEventListener('mouseover', this.documentHoverHandler, true); // Remove hover on mouseout this.documentMouseOutHandler = (event) => this.handleElementMouseOut(event); document.addEventListener('mouseout', this.documentMouseOutHandler, true); // Keyboard shortcuts this.keyboardHandler = (event) => this.handleKeyboard(event); document.addEventListener('keydown', this.keyboardHandler); } handleElementClick(event) { // Check if Ctrl/Cmd is pressed if (!event.ctrlKey && !event.metaKey) return; // Prevent default behavior event.preventDefault(); event.stopPropagation(); const element = event.target; // Don't select our own UI elements if (element.closest('.c4ai-c2c-toolbar') || element.closest('.c4ai-c2c-preview') || element.closest('.c4ai-highlight-box')) { return; } // Toggle element selection if (this.selectedElements.has(element)) { this.deselectElement(element); } else { this.selectElement(element); } this.updateUI(); } handleElementHover(event) { const element = event.target; // Don't hover our own UI elements if (element.closest('.c4ai-c2c-toolbar') || element.closest('.c4ai-c2c-preview') || element.closest('.c4ai-highlight-box') || element.hasAttribute('data-c4ai-badge')) { return; } // Add hover class element.classList.add('c4ai-hover-candidate'); } handleElementMouseOut(event) { const element = event.target; element.classList.remove('c4ai-hover-candidate'); } handleKeyboard(event) { // ESC to deactivate if (event.key === 'Escape') { this.deactivate(); } // Ctrl/Cmd + A to select all visible elements else if ((event.ctrlKey || event.metaKey) && event.key === 'a') { event.preventDefault(); // Select all visible text-containing elements const elements = document.querySelectorAll('p, h1, h2, h3, h4, h5, h6, li, td, th, div, span, article, section'); elements.forEach(el => { if (el.textContent.trim() && this.isVisible(el) && !this.selectedElements.has(el)) { this.selectElement(el); } }); this.updateUI(); } } isVisible(element) { const rect = element.getBoundingClientRect(); const style = window.getComputedStyle(element); return rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'; } selectElement(element) { this.selectedElements.add(element); // Create highlight box const box = this.createHighlightBox(element); this.highlightBoxes.set(element, box); // Add selected class element.classList.add('c4ai-selected'); this.selectionCounter++; } deselectElement(element) { this.selectedElements.delete(element); // Remove highlight box (badge) const badge = this.highlightBoxes.get(element); if (badge) { // Remove scroll/resize listeners if (badge._updatePosition) { window.removeEventListener('scroll', badge._updatePosition, true); window.removeEventListener('resize', badge._updatePosition); } badge.remove(); this.highlightBoxes.delete(element); } // Remove outline element.style.outline = ''; element.style.outlineOffset = ''; // Remove attributes element.removeAttribute('data-c4ai-selection-order'); element.classList.remove('c4ai-selected'); this.selectionCounter--; } createHighlightBox(element) { // Add a data attribute to track selection order element.setAttribute('data-c4ai-selection-order', this.selectionCounter + 1); // Add selection outline directly to the element element.style.outline = '2px solid #0fbbaa'; element.style.outlineOffset = '2px'; // Create badge with fixed positioning const badge = document.createElement('div'); badge.className = 'c4ai-selection-badge-fixed'; badge.textContent = this.selectionCounter + 1; badge.setAttribute('data-c4ai-badge', 'true'); badge.title = 'Click to deselect'; // Get element position and set badge position const rect = element.getBoundingClientRect(); badge.style.cssText = ` position: fixed !important; top: ${rect.top - 12}px !important; left: ${rect.left - 12}px !important; width: 24px !important; height: 24px !important; background: #0fbbaa !important; color: #070708 !important; border-radius: 50% !important; display: flex !important; align-items: center !important; justify-content: center !important; font-size: 12px !important; font-weight: bold !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important; z-index: 999998 !important; cursor: pointer !important; transition: all 0.2s ease !important; pointer-events: auto !important; border: none !important; padding: 0 !important; margin: 0 !important; line-height: 1 !important; text-align: center !important; text-decoration: none !important; box-sizing: border-box !important; `; // Add hover styles dynamically badge.addEventListener('mouseenter', () => { badge.style.setProperty('background', '#ff3c74', 'important'); badge.style.setProperty('transform', 'scale(1.1)', 'important'); }); badge.addEventListener('mouseleave', () => { badge.style.setProperty('background', '#0fbbaa', 'important'); badge.style.setProperty('transform', 'scale(1)', 'important'); }); // Add click handler to badge for deselection badge.addEventListener('click', (e) => { e.stopPropagation(); e.preventDefault(); this.deselectElement(element); this.updateUI(); }); // Add scroll listener to update position const updatePosition = () => { const newRect = element.getBoundingClientRect(); badge.style.top = `${newRect.top - 12}px`; badge.style.left = `${newRect.left - 12}px`; }; // Store the update function so we can remove it later badge._updatePosition = updatePosition; window.addEventListener('scroll', updatePosition, true); window.addEventListener('resize', updatePosition); document.body.appendChild(badge); return badge; } clearSelection() { // Clear all selections this.selectedElements.forEach(element => { // Remove badge const badge = this.highlightBoxes.get(element); if (badge) { // Remove scroll/resize listeners if (badge._updatePosition) { window.removeEventListener('scroll', badge._updatePosition, true); window.removeEventListener('resize', badge._updatePosition); } badge.remove(); } // Remove outline element.style.outline = ''; element.style.outlineOffset = ''; // Remove attributes element.removeAttribute('data-c4ai-selection-order'); element.classList.remove('c4ai-selected'); }); this.selectedElements.clear(); this.highlightBoxes.clear(); this.selectionCounter = 0; this.updateUI(); } updateUI() { const count = this.selectedElements.size; // Update selection count this.toolbar.querySelector('.c4ai-selection-count').textContent = `${count} element${count !== 1 ? 's' : ''} selected`; // Enable/disable buttons const hasSelection = count > 0; this.toolbar.querySelector('.c4ai-preview-btn').disabled = !hasSelection; this.toolbar.querySelector('.c4ai-copy-btn').disabled = !hasSelection; this.toolbar.querySelector('.c4ai-clear-btn').disabled = !hasSelection; } async showPreview() { // Initialize markdown preview modal if not already done if (!this.markdownPreviewModal) { this.markdownPreviewModal = new MarkdownPreviewModal(); } // Show modal with callback to generate markdown this.markdownPreviewModal.show(async (options) => { return await this.generateMarkdown(options); }); } /* createPreviewPanel() { this.previewPanel = document.createElement('div'); this.previewPanel.className = 'c4ai-c2c-preview'; this.previewPanel.innerHTML = `${this.escapeHtml(markdown)}`;
// Update preview pane using marked.js
const previewPane = this.previewPanel.querySelector('[data-pane="preview"]');
// Configure marked options (marked.js is already loaded via manifest)
if (window.marked) {
marked.setOptions({
gfm: true,
breaks: true,
tables: true,
headerIds: false,
mangle: false
});
// Render markdown to HTML
const html = marked.parse(markdown);
previewPane.innerHTML = `${this.escapeHtml(markdown)}