generated from coulomb/repo-seed
Complete integration of refactored testdrive-jsui capability: ## Refactored Architecture - js/ - All JavaScript source (controls, components, core) - static/ - CSS, images, templates - src/testdrive_jsui/ - Python package - tests/ - Python tests ## Plugin Self-Declaration - get_plugin_source_dir() - plugin declares own location - get_asset_paths() - organized asset paths - No hardcoded discovery logic ## Merged Content - Baseline UI scaffold (tutorials, LICENSE, INTRODUCTION.md) - Refactored capability implementation - Comprehensive documentation Ready for standalone use or integration with markitect. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
287 lines
11 KiB
JavaScript
287 lines
11 KiB
JavaScript
/**
|
|
* Main Markitect JavaScript Entry Point - Clean Architecture Version
|
|
*
|
|
* Uses ONLY the JSON configuration interface - NO Python-generated JavaScript!
|
|
* Initializes all controls and systems when document is ready
|
|
* Implements graceful degradation for missing dependencies
|
|
*/
|
|
|
|
// Main application module
|
|
const MarkitectMain = {
|
|
initialized: false,
|
|
config: null,
|
|
|
|
// Initialize the complete application
|
|
initialize: function() {
|
|
if (this.initialized) {
|
|
console.log('⚠️ MarkitectMain already initialized, skipping');
|
|
return;
|
|
}
|
|
|
|
console.log('🚀 MarkitectMain initializing...');
|
|
|
|
try {
|
|
// Get configuration - if not loaded, use defaults
|
|
this.config = window.markitectConfig;
|
|
if (!this.config || !this.config.loaded) {
|
|
console.warn('⚠️ Configuration not loaded, proceeding with defaults');
|
|
this.config = {
|
|
markdownContent: document.querySelector('#markdown-content')?.textContent || '',
|
|
mode: 'edit',
|
|
theme: 'github'
|
|
};
|
|
}
|
|
|
|
// Initialize core systems
|
|
this.initializeCoreComponents();
|
|
this.initializeControlPanels();
|
|
this.setupEventHandlers();
|
|
this.renderContent();
|
|
|
|
this.initialized = true;
|
|
console.log('✅ MarkitectMain initialization complete');
|
|
|
|
} catch (error) {
|
|
console.error('❌ MarkitectMain initialization failed:', error);
|
|
this.fallbackMode();
|
|
}
|
|
},
|
|
|
|
// Initialize core modular components
|
|
initializeCoreComponents: function() {
|
|
console.log('🔧 Initializing core components...');
|
|
|
|
const container = document.getElementById('markdown-content') || document.body;
|
|
|
|
// Initialize section manager
|
|
if (typeof SectionManager !== 'undefined') {
|
|
this.sectionManager = new SectionManager();
|
|
console.log('✅ SectionManager initialized');
|
|
} else {
|
|
throw new Error('SectionManager not available');
|
|
}
|
|
|
|
// Initialize DOM renderer
|
|
if (typeof DOMRenderer !== 'undefined') {
|
|
this.domRenderer = new DOMRenderer(this.sectionManager, container);
|
|
console.log('✅ DOMRenderer initialized');
|
|
} else {
|
|
throw new Error('DOMRenderer not available');
|
|
}
|
|
|
|
// Initialize debug panel
|
|
if (typeof DebugPanel !== 'undefined') {
|
|
this.debugPanel = new DebugPanel();
|
|
console.log('✅ DebugPanel initialized');
|
|
}
|
|
|
|
// Initialize document controls
|
|
if (typeof DocumentControls !== 'undefined') {
|
|
this.documentControls = new DocumentControls();
|
|
this.documentControls.create();
|
|
console.log('✅ DocumentControls initialized');
|
|
}
|
|
},
|
|
|
|
// Initialize enhanced control panels with compass positioning
|
|
initializeControlPanels: function() {
|
|
console.log('🎛️ Initializing enhanced control panels with compass positioning...');
|
|
|
|
// ContentsControl (Northwest)
|
|
if (typeof ContentsControl !== 'undefined') {
|
|
this.contentsControl = new ContentsControl();
|
|
this.contentsControl.config.position = 'nw';
|
|
this.contentsControl.show();
|
|
window.contentsControl = this.contentsControl;
|
|
console.log('✅ ContentsControl initialized (Northwest) with enhanced ControlBase');
|
|
}
|
|
|
|
// StatusControl (East)
|
|
if (typeof StatusControl !== 'undefined') {
|
|
this.statusControl = new StatusControl();
|
|
this.statusControl.config.position = 'e';
|
|
this.statusControl.show();
|
|
window.statusControl = this.statusControl;
|
|
console.log('✅ StatusControl initialized (East) with enhanced ControlBase');
|
|
}
|
|
|
|
// DebugControl (Southeast)
|
|
if (typeof DebugControl !== 'undefined') {
|
|
this.debugControl = new DebugControl();
|
|
this.debugControl.config.position = 'se';
|
|
this.debugControl.show();
|
|
window.debugControl = this.debugControl;
|
|
console.log('✅ DebugControl initialized (Southeast) with enhanced ControlBase');
|
|
}
|
|
|
|
// EditControl (Northeast)
|
|
if (typeof EditControl !== 'undefined') {
|
|
this.editControl = new EditControl();
|
|
this.editControl.config.position = 'ne';
|
|
this.editControl.show();
|
|
window.editControl = this.editControl;
|
|
console.log('✅ EditControl initialized (Northeast) with enhanced ControlBase');
|
|
}
|
|
},
|
|
|
|
// Setup event handlers
|
|
setupEventHandlers: function() {
|
|
console.log('🔌 Setting up event handlers...');
|
|
|
|
if (!this.documentControls) return;
|
|
|
|
this.documentControls.setEventHandlers({
|
|
'save-document': () => {
|
|
console.log('💾 Save document clicked');
|
|
try {
|
|
const currentMarkdown = this.sectionManager.getDocumentMarkdown();
|
|
const now = new Date();
|
|
const timestamp = now.toISOString().slice(0, 19).replace(/:/g, '-').replace('T', '-');
|
|
const filename = `${this.config.originalFilename}-edited-${timestamp}.md`;
|
|
|
|
const blob = new Blob([currentMarkdown], { type: 'text/markdown' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
|
|
if (this.debugPanel) {
|
|
this.debugPanel.addMessage(`Document saved as: ${filename}`, 'SUCCESS');
|
|
}
|
|
console.log(`✅ Document saved as: ${filename}`);
|
|
|
|
} catch (error) {
|
|
if (this.debugPanel) {
|
|
this.debugPanel.addMessage(`Save failed: ${error.message}`, 'ERROR');
|
|
}
|
|
console.error('❌ Save error:', error);
|
|
}
|
|
},
|
|
|
|
'reset-all': () => {
|
|
console.log('🔄 Reset all clicked');
|
|
try {
|
|
this.domRenderer.hideCurrentEditor();
|
|
const allSections = Array.from(this.sectionManager.sections.values());
|
|
allSections.forEach(section => section.resetToOriginal());
|
|
this.domRenderer.renderAllSections(allSections);
|
|
|
|
if (this.debugPanel) {
|
|
this.debugPanel.addMessage('Reset all sections to original state', 'INFO');
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ Reset all failed:', error);
|
|
}
|
|
},
|
|
|
|
'show-status': () => {
|
|
const status = this.sectionManager.getDocumentStatus();
|
|
alert(`Document Status:\nTotal Sections: ${status.totalSections}\nEditing Sections: ${status.editingSections}`);
|
|
},
|
|
|
|
'toggle-debug': () => {
|
|
if (this.debugPanel) {
|
|
this.debugPanel.toggle();
|
|
}
|
|
}
|
|
});
|
|
|
|
// Setup section manager event handlers
|
|
if (this.sectionManager && this.debugPanel) {
|
|
this.sectionManager.on('sections-created', (data) => {
|
|
this.debugPanel.addMessage(`Created ${data.count} sections`, 'INFO');
|
|
});
|
|
|
|
this.sectionManager.on('edit-started', (data) => {
|
|
this.debugPanel.addMessage(`Edit started for section: ${data.sectionId}`, 'DEBUG');
|
|
});
|
|
|
|
this.sectionManager.on('changes-accepted', (data) => {
|
|
this.debugPanel.addMessage(`Changes accepted for section: ${data.sectionId}`, 'SUCCESS');
|
|
this.updateSectionDOM(data.sectionId);
|
|
});
|
|
|
|
this.sectionManager.on('changes-cancelled', (data) => {
|
|
this.debugPanel.addMessage(`Changes cancelled for section: ${data.sectionId}`, 'WARNING');
|
|
});
|
|
}
|
|
},
|
|
|
|
// Render content using the configuration
|
|
renderContent: function() {
|
|
console.log('📄 Rendering markdown content...');
|
|
|
|
const markdownToRender = this.config.markdownContent || '';
|
|
if (markdownToRender.trim()) {
|
|
const sections = this.sectionManager.createSectionsFromMarkdown(markdownToRender);
|
|
this.domRenderer.renderAllSections(sections);
|
|
|
|
if (this.debugPanel) {
|
|
this.debugPanel.addMessage(`Initialized with ${sections.length} sections`, 'INFO');
|
|
}
|
|
console.log(`✅ Rendered ${sections.length} sections`);
|
|
} else {
|
|
if (this.debugPanel) {
|
|
this.debugPanel.addMessage('No markdown content to initialize', 'WARNING');
|
|
}
|
|
console.warn('⚠️ No markdown content to render');
|
|
}
|
|
},
|
|
|
|
// Update section DOM after changes
|
|
updateSectionDOM: function(sectionId) {
|
|
try {
|
|
const section = this.sectionManager.sections.get(sectionId);
|
|
if (section) {
|
|
const sectionElement = this.domRenderer.findSectionElement(sectionId);
|
|
if (sectionElement) {
|
|
const newElement = this.domRenderer.renderSection(section);
|
|
sectionElement.parentNode.replaceChild(newElement, sectionElement);
|
|
|
|
if (this.debugPanel) {
|
|
this.debugPanel.addMessage(`DOM updated for section: ${sectionId}`, 'INFO');
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ Failed to update section DOM:', error);
|
|
}
|
|
},
|
|
|
|
// Fallback mode if initialization fails
|
|
fallbackMode: function() {
|
|
console.warn('⚠️ Running in fallback mode');
|
|
|
|
// Basic content rendering fallback
|
|
const contentDiv = document.getElementById('markdown-content');
|
|
if (contentDiv && this.config && this.config.markdownContent) {
|
|
const basicHtml = this.config.markdownContent
|
|
.replace(/^# (.*$)/gim, '<h1>$1</h1>')
|
|
.replace(/^## (.*$)/gim, '<h2>$1</h2>')
|
|
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
|
|
.replace(/\n\n/g, '</p><p>')
|
|
.replace(/\n/g, '<br>');
|
|
|
|
contentDiv.innerHTML = `<p>${basicHtml}</p>`;
|
|
console.log('✅ Fallback content rendered');
|
|
}
|
|
}
|
|
};
|
|
|
|
// Make components globally available for debugging
|
|
window.MarkitectMain = MarkitectMain;
|
|
|
|
// Auto-initialize when DOM is ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Small delay to ensure config is loaded
|
|
setTimeout(() => MarkitectMain.initialize(), 100);
|
|
});
|
|
} else {
|
|
// DOM already ready
|
|
setTimeout(() => MarkitectMain.initialize(), 100);
|
|
} |