Files
markitect-main/testdrive-jsui/static/js/main.js
tegwick 8ef356af57 feat: implement plugin infrastructure for rendering engines
Added comprehensive plugin system for independent JavaScript UI development:

**Plugin Infrastructure:**
- Extended existing MarkiTect plugin system with RenderingEnginePlugin base class
- Added RENDERING plugin type to PluginType enum
- Created RenderingConfig for asset management and deployment
- Implemented RenderingEngineManager for plugin discovery and lifecycle

**TestDrive JSUI Plugin:**
- Extracted JavaScript UI components to independent testdrive-jsui plugin
- Created standalone development environment (no Python required)
- Implemented compass-positioned control panels (NW, NE, E, SE)
- Added clean JSON configuration interface for Python↔JavaScript data transfer

**Asset Management:**
- Development mode: serve assets directly from plugin source directory
- Production mode: deploy to _markitect/plugins/[plugin-name]/ structure
- Configurable asset URLs and deployment strategies
- Support for external dependencies (CDN resources)

**Standalone Development:**
- testdrive-jsui/test.html for browser-based development
- Package.json with npm scripts for development server
- Complete separation of JavaScript development from Python environment
- Hot reload and standard web development workflow

**Integration Demo:**
- demo_plugin_integration.py showcasing all plugin capabilities
- Standalone, plugin discovery, production deployment examples
- Asset URL generation for different deployment modes

This enables JavaScript-first development while maintaining clean integration
with the MarkiTect Python ecosystem. Developers can now work on UI components
independently using standard web development tools and workflows.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 06:49:41 +01:00

201 lines
7.8 KiB
JavaScript

/**
* Main Markitect JavaScript Entry Point
* Initializes all controls and systems when document is ready
* Implements graceful degradation for missing dependencies
* Supports Fail Fast strict mode for development
*/
// Development mode detection
const MARKITECT_STRICT_MODE = (
window.location.hostname === 'localhost' ||
window.location.hostname === '127.0.0.1' ||
window.location.search.includes('strict=true') ||
window.markitectStrictMode === true
);
// Utility functions for safe initialization
const MarkitectMain = {
// Safe dependency checking with timeout
checkDependencies: function() {
const dependencies = {
debugSystem: !!window.MarkitectDebugSystem,
control: !!window.Control,
statusControl: !!window.StatusControl,
debugControl: !!window.DebugControl,
contentsControl: !!window.ContentsControl,
editControl: !!window.EditControl
};
console.log('📋 Dependency check results:', dependencies);
return dependencies;
},
// Safe logging that works even without debug system
safeLog: function(message, level = 'INFO', component = 'Main', data = {}) {
console.log(`[${level}] ${component}: ${message}`);
// In strict mode, throw on errors for immediate development feedback
if (MARKITECT_STRICT_MODE && level === 'ERROR') {
console.error(`🚨 STRICT MODE: Throwing error for immediate diagnosis`);
throw new Error(`${component}: ${message}`);
}
// Try to use debug system if available
if (window.MarkitectDebugSystem && window.MarkitectDebugSystem.addMessage) {
try {
window.MarkitectDebugSystem.addMessage(message, level, component, { ...data, eventType: 'SYSTEM' });
} catch (error) {
console.warn('Debug system logging failed:', error);
if (MARKITECT_STRICT_MODE) {
throw error; // Fail fast in development
}
}
}
},
// Safe control initialization with fallbacks
initializeControl: function(controlClass, controlName, icon = '🔧') {
const timeout = setTimeout(() => {
const message = `${controlName} initialization timed out`;
console.warn(message);
if (MARKITECT_STRICT_MODE) {
throw new Error(message); // Fail fast in development
}
}, 5000);
try {
if (!controlClass) {
const message = `${controlName} class not available, skipping`;
this.safeLog(message, MARKITECT_STRICT_MODE ? 'ERROR' : 'WARNING');
clearTimeout(timeout);
return null;
}
const controlInstance = new controlClass();
if (!controlInstance || typeof controlInstance.createControl !== 'function') {
throw new Error(`Invalid ${controlName} instance`);
}
const element = controlInstance.createControl();
if (!element) {
throw new Error(`${controlName} failed to create element`);
}
clearTimeout(timeout);
this.safeLog(`${controlName} initialized successfully`, 'SUCCESS');
return controlInstance;
} catch (error) {
clearTimeout(timeout);
this.safeLog(`${controlName} initialization failed: ${error.message}`, 'ERROR');
// Create minimal fallback control if core Control class exists
if (window.Control && controlName === 'StatusControl') {
return this.createFallbackControl(controlName, icon);
}
return null;
}
},
// Create minimal fallback control for essential controls
createFallbackControl: function(name, icon) {
try {
const fallback = Object.create(window.Control);
fallback.config = {
icon: icon,
title: `${name} (Fallback)`,
className: `${name.toLowerCase()}-fallback`,
defaultContent: `${name} is running in fallback mode due to initialization issues.`,
ariaLabel: `${name} Fallback Control`,
position: 'e'
};
const element = fallback.createControl();
if (element) {
this.safeLog(`${name} fallback control created`, 'INFO');
return { control: fallback };
}
} catch (error) {
this.safeLog(`Fallback control creation failed: ${error.message}`, 'ERROR');
}
return null;
},
// Main initialization with comprehensive error handling
initialize: function() {
this.safeLog('🚀 Initializing Markitect controls and systems...', 'INFO');
// Check dependencies first
const deps = this.checkDependencies();
if (!deps.control) {
this.safeLog('❌ Core Control system not available, cannot initialize UI controls', 'ERROR');
return;
}
const initializedControls = {};
let successCount = 0;
let totalAttempts = 0;
// Initialize controls with graceful degradation
const controlsToInit = [
{ class: window.StatusControl, name: 'StatusControl', key: 'statusControl', icon: '📊', essential: true },
{ class: window.DebugControl, name: 'DebugControl', key: 'debugControl', icon: '🪲', essential: false },
{ class: window.ContentsControl, name: 'ContentsControl', key: 'contentsControl', icon: '☰', essential: false },
{ class: window.EditControl, name: 'EditControl', key: 'editControl', icon: '✏️', essential: false }
];
controlsToInit.forEach(({ class: controlClass, name, key, icon, essential }) => {
totalAttempts++;
const instance = this.initializeControl(controlClass, name, icon);
if (instance) {
initializedControls[key] = instance.control || instance;
window[key] = initializedControls[key];
successCount++;
} else if (essential) {
this.safeLog(`Essential control ${name} failed to initialize`, 'ERROR');
}
});
// Report initialization results
const successRate = Math.round((successCount / totalAttempts) * 100);
if (successCount === totalAttempts) {
this.safeLog('✅ All controls initialized successfully', 'SUCCESS');
} else if (successCount > 0) {
this.safeLog(`⚠️ Partial initialization: ${successCount}/${totalAttempts} controls (${successRate}%) initialized`, 'WARNING');
} else {
this.safeLog('❌ No controls could be initialized', 'ERROR');
}
// Set up global error handlers for runtime protection
this.setupErrorHandlers();
this.safeLog(`✅ Markitect initialization complete (${successCount}/${totalAttempts} controls active)`, 'INFO');
},
// Set up global error handlers
setupErrorHandlers: function() {
// Catch unhandled errors
window.addEventListener('error', (event) => {
this.safeLog(`Unhandled error: ${event.message} at ${event.filename}:${event.lineno}`, 'ERROR');
});
// Catch unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
this.safeLog(`Unhandled promise rejection: ${event.reason}`, 'ERROR');
event.preventDefault(); // Prevent console spam
});
}
};
// Initialize when DOM is ready with additional safety
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => MarkitectMain.initialize(), 100); // Brief delay for dependencies
});
} else {
// DOM already loaded
setTimeout(() => MarkitectMain.initialize(), 100);
}