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>
201 lines
7.8 KiB
JavaScript
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);
|
|
} |