/** * Clean Editor Integration * * This file provides a complete, drop-in replacement for the existing * section editing functionality using our clean object-oriented architecture. */ // Include the core classes (in real implementation, these would be imported) // For now, assuming they're loaded separately /** * MarkitectCleanEditor - Main integration class * * This replaces the existing complex editor implementation with our * clean, testable architecture. */ class MarkitectCleanEditor { constructor(markdownContent, containerElement, options = {}) { this.options = { theme: 'github', keyboardShortcuts: true, autosave: false, ...options }; // Initialize the core system this.sectionManager = new MarkitectEditor.SectionManager(); this.domRenderer = new MarkitectEditor.DOMRenderer(this.sectionManager, containerElement); // Store original content this.originalMarkdown = markdownContent; // Event handlers for external integration this.onDocumentChange = null; this.onSectionChange = null; // Setup external event forwarding this.setupEventForwarding(); // Initialize sections this.initialize(); } /** * Initialize the editor with the markdown content */ initialize() { try { // Create sections from markdown const sections = this.sectionManager.createSectionsFromMarkdown(this.originalMarkdown); console.log(`✓ Initialized clean editor with ${sections.length} sections`); // Set up global keyboard shortcuts this.setupGlobalKeyboardShortcuts(); return true; } catch (error) { console.error('Failed to initialize clean editor:', error); return false; } } /** * Setup event forwarding to external callbacks */ setupEventForwarding() { // Forward document-level changes this.sectionManager.on('changes-accepted', () => { if (this.onDocumentChange) { this.onDocumentChange(this.getDocumentStatus()); } }); this.sectionManager.on('changes-cancelled', () => { if (this.onDocumentChange) { this.onDocumentChange(this.getDocumentStatus()); } }); // Forward section-level changes this.sectionManager.on('content-updated', (data) => { if (this.onSectionChange) { this.onSectionChange(data); } }); } /** * Setup global keyboard shortcuts */ setupGlobalKeyboardShortcuts() { if (!this.options.keyboardShortcuts) return; document.addEventListener('keydown', (event) => { if (event.ctrlKey || event.metaKey) { switch (event.key) { case 's': event.preventDefault(); this.saveDocument(); break; case 'z': if (event.shiftKey) { event.preventDefault(); // Could implement redo } else { event.preventDefault(); // Could implement undo } break; case 'r': event.preventDefault(); this.resetAllSections(); break; } } }); } /** * Get current document markdown * @returns {string} The complete document markdown */ getDocumentMarkdown() { return this.sectionManager.getDocumentMarkdown(); } /** * Get document status * @returns {Object} Document status object */ getDocumentStatus() { return this.sectionManager.getDocumentStatus(); } /** * Check if document has unsaved changes * @returns {boolean} True if there are unsaved changes */ hasUnsavedChanges() { const status = this.getDocumentStatus(); return status.hasUnsavedChanges; } /** * Save document (triggers download) */ saveDocument() { const markdown = this.getDocumentMarkdown(); const status = this.getDocumentStatus(); // Create download const blob = new Blob([markdown], { type: 'text/markdown' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'document.md'; a.style.display = 'none'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); console.log('📄 Document saved:', status); this.showMessage('Document saved successfully!', 'success'); } /** * Reset all sections to original state */ resetAllSections() { if (confirm('Reset all content to original markdown? This will lose all edits.')) { this.domRenderer.resetAllSections(); this.showMessage('All sections reset to original content', 'info'); } } /** * Show message to user * @param {string} message - The message to show * @param {string} type - Message type: 'success', 'error', 'info' */ showMessage(message, type = 'info') { // Create message element const messageDiv = document.createElement('div'); messageDiv.className = `markitect-message markitect-message-${type}`; messageDiv.textContent = message; // Style the message const styles = { 'success': { bg: '#4caf50', color: 'white' }, 'error': { bg: '#f44336', color: 'white' }, 'info': { bg: '#2196f3', color: 'white' } }; const style = styles[type] || styles.info; messageDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${style.bg}; color: ${style.color}; padding: 12px 20px; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.2); z-index: 10000; font-size: 14px; max-width: 300px; animation: slideIn 0.3s ease; `; // Add animation const styleSheet = document.createElement('style'); styleSheet.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } `; document.head.appendChild(styleSheet); document.body.appendChild(messageDiv); // Remove after 3 seconds setTimeout(() => { if (messageDiv.parentNode) { messageDiv.style.animation = 'slideIn 0.3s ease reverse'; setTimeout(() => { if (messageDiv.parentNode) { messageDiv.parentNode.removeChild(messageDiv); } }, 300); } }, 3000); } /** * Add control panel to the page */ addControlPanel() { const panel = document.createElement('div'); panel.className = 'markitect-control-panel-clean'; panel.innerHTML = `

Document Editor

Ready
`; // Style the panel panel.style.cssText = ` position: fixed; top: 20px; left: 20px; background: white; border: 1px solid #ddd; border-radius: 8px; padding: 16px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); z-index: 1000; font-family: system-ui, -apple-system, sans-serif; min-width: 200px; `; // Add styles for buttons const styles = document.createElement('style'); styles.textContent = ` .control-header h3 { margin: 0 0 8px 0; font-size: 16px; } .control-status { font-size: 12px; color: #666; } .control-actions { margin-top: 12px; } .control-btn { display: block; width: 100%; margin: 4px 0; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; background: white; cursor: pointer; font-size: 14px; } .control-btn.primary { background: #2196f3; color: white; border-color: #2196f3; } .control-btn.secondary { background: #ff9800; color: white; border-color: #ff9800; } .control-btn:hover { opacity: 0.9; } `; document.head.appendChild(styles); document.body.appendChild(panel); // Add event listeners panel.querySelector('#clean-save').addEventListener('click', () => this.saveDocument()); panel.querySelector('#clean-reset').addEventListener('click', () => this.resetAllSections()); panel.querySelector('#clean-status-btn').addEventListener('click', () => this.showStatusDialog()); // Update status periodically setInterval(() => { const status = this.getDocumentStatus(); const statusEl = panel.querySelector('#clean-status'); if (status.hasUnsavedChanges) { statusEl.textContent = `${status.modifiedSections} sections modified`; statusEl.style.color = '#ff9800'; } else { statusEl.textContent = 'All changes saved'; statusEl.style.color = '#4caf50'; } }, 1000); } /** * Show status dialog */ showStatusDialog() { const status = this.getDocumentStatus(); const message = ` Document Status: • Total sections: ${status.totalSections} • Modified sections: ${status.modifiedSections} • Editing sections: ${status.editingSections} • Saved sections: ${status.savedSections} • Has unsaved changes: ${status.hasUnsavedChanges ? 'Yes' : 'No'} `.trim(); alert(message); } /** * Destroy the editor and clean up */ destroy() { if (this.domRenderer) { this.domRenderer.destroy(); } // Remove control panel const panel = document.querySelector('.markitect-control-panel-clean'); if (panel) { panel.remove(); } } } // CSS for the clean editor const CLEAN_EDITOR_CSS = ` /* Clean Section Editor Styles */ .markitect-section-editable { margin: 16px 0; padding: 12px; border-radius: 6px; cursor: pointer; transition: all 0.2s ease; border: 2px solid transparent; } .markitect-section-editable:hover { background-color: rgba(33, 150, 243, 0.05); border-color: rgba(33, 150, 243, 0.2); } .markitect-section-editable.section-editing { background-color: rgba(33, 150, 243, 0.1); border-color: #2196f3; } .markitect-section-editable.section-modified { background-color: rgba(255, 235, 59, 0.1); border-left: 4px solid #ffc107; } .markitect-section-editable.section-saved { background-color: rgba(76, 175, 80, 0.1); border-left: 4px solid #4caf50; } /* Edit container layout */ .markitect-edit-container { display: flex; gap: 12px; align-items: flex-start; width: 100%; } .markitect-textarea-wrapper { flex: 1; min-width: 0; } .edit-mode { width: 100%; min-height: 60px; max-height: 360px; font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', monospace; border: 2px solid #007acc; border-radius: 6px; padding: 12px; font-size: 14px; line-height: 1.5; resize: vertical; overflow: auto; box-sizing: border-box; transition: height 0.15s ease; background: white; } .edit-mode:focus { outline: none; border-color: #1976d2; box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1); } /* Section controls */ .markitect-section-controls { display: flex; flex-direction: column; gap: 6px; padding: 8px; background: rgba(255, 255, 255, 0.95); border: 1px solid #e0e0e0; border-radius: 6px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 80px; flex-shrink: 0; } .markitect-section-btn { padding: 8px 12px; border: none; border-radius: 4px; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; justify-content: center; gap: 4px; white-space: nowrap; min-height: 32px; } .markitect-section-btn.accept { background: #4caf50; color: white; } .markitect-section-btn.accept:hover { background: #45a049; } .markitect-section-btn.cancel { background: #f44336; color: white; } .markitect-section-btn.cancel:hover { background: #da190b; } .markitect-section-btn.reset { background: #ff9800; color: white; } .markitect-section-btn.reset:hover { background: #f57c00; } /* Responsive design */ @media (max-width: 768px) { .markitect-edit-container { flex-direction: column; gap: 8px; } .markitect-section-controls { flex-direction: row; justify-content: center; min-width: auto; } .edit-mode { min-width: 100%; } } @media (max-width: 480px) { .markitect-section-controls { flex-wrap: wrap; gap: 4px; } .markitect-section-btn { flex: 1; min-width: 70px; } } `; // Auto-inject CSS when loaded if (typeof document !== 'undefined') { const style = document.createElement('style'); style.textContent = CLEAN_EDITOR_CSS; document.head.appendChild(style); } // Export for use if (typeof module !== 'undefined' && module.exports) { module.exports = { MarkitectCleanEditor }; } else { window.MarkitectEditor = window.MarkitectEditor || {}; window.MarkitectEditor.MarkitectCleanEditor = MarkitectCleanEditor; }