UNPKG

superdesign-claude

Version:

SuperDesign Claude v2.2 - AI-Powered Design Pattern Recognition Engine with Code Generation - PHASE 1 + CODE GEN

1,285 lines (1,128 loc) 47.7 kB
#!/usr/bin/env node /** * SuperDesign Claude v2.0 - Enhanced Pattern Recognition Engine with Production Error Handling * * Responsible for: * - Matching intents to optimal UI patterns * - Generating smart ASCII wireframes automatically * - Suggesting optimal components for each pattern * - Calculating optimal layout structures using mathematical principles * - Comprehensive error handling and recovery * * Phase 1 Implementation: Week 1, Day 3-4 (Production Ready) */ const fs = require('fs'); const path = require('path'); const { ErrorHandler, PatternRecognitionError, ValidationError } = require('../../utils/ErrorHandler'); const { LoggingManager } = require('../../utils/LoggingManager'); class PatternRecognitionEngine { constructor(options = {}) { try { // Initialize logging and error handling this.logger = options.logger || new LoggingManager(); this.errorHandler = options.errorHandler || new ErrorHandler(this.logger); // Validate options this.validateOptions(options); // Load configuration with error handling this.patterns = this.safeLoad(() => this.loadPatterns(), 'patterns database'); this.layoutTemplates = this.safeLoad(() => this.loadLayoutTemplates(), 'layout templates'); this.componentRelationships = this.safeLoad(() => this.loadComponentRelationships(), 'component relationships'); // Performance metrics this.metrics = { totalRecognitions: 0, successfulMatches: 0, averageMatchScore: 0, errors: 0, recoveredErrors: 0 }; // Circuit breaker for repeated failures this.circuitBreaker = { isOpen: false, failures: 0, lastFailure: null, threshold: options.circuitBreakerThreshold || 5, resetTimeout: options.circuitBreakerTimeout || 60000 }; this.logger.info('Pattern Recognition Engine initialized successfully'); } catch (error) { const handledError = this.handleInitializationError(error); throw handledError; } } /** * Validate constructor options */ validateOptions(options) { if (options.circuitBreakerThreshold && (typeof options.circuitBreakerThreshold !== 'number' || options.circuitBreakerThreshold < 1)) { throw new ValidationError('circuitBreakerThreshold must be a positive number', 'circuitBreakerThreshold', options.circuitBreakerThreshold); } if (options.circuitBreakerTimeout && (typeof options.circuitBreakerTimeout !== 'number' || options.circuitBreakerTimeout < 1000)) { throw new ValidationError('circuitBreakerTimeout must be at least 1000ms', 'circuitBreakerTimeout', options.circuitBreakerTimeout); } } /** * Safe loading wrapper with error handling */ safeLoad(loadFunction, resourceName) { try { const result = loadFunction(); if (!result) { throw new Error(`Invalid ${resourceName} structure`); } return result; } catch (error) { this.logger.error(`Failed to load ${resourceName}`, error); throw new PatternRecognitionError(`Failed to initialize ${resourceName}: ${error.message}`); } } /** * Safe execution wrapper for synchronous operations */ safeExecute(operation, operationName, requestId) { try { return operation(); } catch (error) { const errorInfo = this.errorHandler.handle(error, { component: 'PatternRecognitionEngine', operation: operationName, requestId }); // Attempt recovery const recovery = this.errorHandler.executeRecovery(errorInfo); if (recovery && recovery.success && recovery.result) { this.metrics.recoveredErrors++; return recovery.result; } throw error; } } /** * Safe execution wrapper for asynchronous operations */ async safeExecuteAsync(operation, operationName, requestId) { try { return await operation(); } catch (error) { const errorInfo = this.errorHandler.handle(error, { component: 'PatternRecognitionEngine', operation: operationName, requestId }); // Attempt recovery const recovery = await this.errorHandler.executeRecovery(errorInfo, { fallbackFunction: () => this.getFallbackResult(operationName) }); if (recovery && recovery.success) { this.metrics.recoveredErrors++; return recovery.result || this.getFallbackResult(operationName); } throw error; } } /** * Get fallback results for failed operations */ getFallbackResult(operationName) { const fallbacks = { 'pattern matching': [{ id: 'fallback-general', name: 'General Layout', type: 'general', score: 1, confidence: 0.3, intents: { primary: ['general'], subtypes: [] }, styles: { compatible: ['modern'] }, components: { core: ['navigation'], optional: [] }, layout: { template: 'landing-hero' }, complexity: 3 }], 'wireframe generation': { ascii: '┌──────────────┐\n│ HEADER │\n├──────────────┤\n│ │\n│ MAIN │\n│ │\n├──────────────┤\n│ FOOTER │\n└──────────────┘', structure: { sections: [{ type: 'main', components: [] }] }, dimensions: { width: 40, height: 10 } }, 'layout optimization': { type: 'css-grid', grid: { columns: 12, gutters: 16, rows: 'auto' }, breakpoints: { mobile: 375, tablet: 768, desktop: 1024 }, spacing: { scale: [8, 16, 24, 32], unit: 'px' } }, 'component suggestion': { required: ['navigation'], recommended: ['card'], alternative: [] } }; return fallbacks[operationName] || {}; } /** * Generate unique request ID for tracking */ generateRequestId() { return `pattern_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * Validate recognition input */ validateInput(intent, options, requestId) { if (!intent || typeof intent !== 'object') { throw new ValidationError('Intent must be an object', 'intent', intent); } if (!intent.intent || typeof intent.intent !== 'object') { throw new ValidationError('Intent must have an intent property', 'intent.intent', intent.intent); } if (!intent.intent.type || typeof intent.intent.type !== 'string') { throw new ValidationError('Intent type must be a string', 'intent.intent.type', intent.intent.type); } if (options && typeof options !== 'object') { throw new ValidationError('Options must be an object', 'options', options); } this.logger.debug('Input validation passed', { requestId, intentType: intent.intent.type }); } /** * Handle recognition errors with comprehensive recovery */ async handleRecognitionError(error, requestId, intent, options, startTime) { const recognitionTime = performance.now() - startTime; this.metrics.errors++; // Update circuit breaker this.circuitBreaker.failures++; this.circuitBreaker.lastFailure = Date.now(); if (this.circuitBreaker.failures >= this.circuitBreaker.threshold) { this.circuitBreaker.isOpen = true; this.logger.error('Circuit breaker opened due to repeated failures', { requestId, failures: this.circuitBreaker.failures, threshold: this.circuitBreaker.threshold }); } // Handle the error const errorInfo = this.errorHandler.handle(error, { component: 'PatternRecognitionEngine', operation: 'recognize', requestId, intentType: intent?.intent?.type || 'unknown' }); this.logger.error('Pattern recognition failed', error, { requestId, recognitionTime, errorType: errorInfo.type, recoverable: errorInfo.recoverable }); // Attempt recovery if (errorInfo.recoverable) { try { const recovery = await this.errorHandler.executeRecovery(errorInfo, { fallbackFunction: () => this.createFallbackRecognition(intent, options, requestId) }); if (recovery && recovery.success) { this.metrics.recoveredErrors++; this.logger.info('Pattern recognition recovered with fallback', { requestId }); return recovery.result; } } catch (recoveryError) { this.logger.error('Recovery failed', recoveryError, { requestId }); } } // Create user-friendly error message const userMessage = this.errorHandler.createUserMessage(errorInfo); if (userMessage) { console.error(userMessage); } // Re-throw with enhanced context const enhancedError = new PatternRecognitionError( `Pattern recognition failed: ${error.message}. Request ID: ${requestId}` ); enhancedError.requestId = requestId; enhancedError.originalError = error; enhancedError.recognitionTime = recognitionTime; throw enhancedError; } /** * Create fallback recognition result */ createFallbackRecognition(intent, options, requestId) { this.logger.info('Creating fallback recognition', { requestId }); const fallbackPattern = { id: 'fallback-general', name: 'General Layout', type: intent?.intent?.type || 'general', score: 1, confidence: 0.3 }; return { selectedPattern: fallbackPattern, wireframe: this.getFallbackResult('wireframe generation'), layout: this.getFallbackResult('layout optimization'), components: this.getFallbackResult('component suggestion'), layoutMetrics: { complexity: 0.3, accessibility: 0.7, performance: 0.8, responsiveness: 0.5 }, alternatives: [], metadata: { recognitionTime: 0, patternsEvaluated: 0, timestamp: new Date().toISOString(), fallbackUsed: true, requestId } }; } /** * Handle initialization errors */ handleInitializationError(error) { console.error('Pattern Recognition Engine initialization failed:', error.message); if (error instanceof ValidationError) { return error; } return new PatternRecognitionError(`Initialization failed: ${error.message}`); } /** * Reset circuit breaker on successful operation */ resetCircuitBreaker() { if (this.circuitBreaker.failures > 0) { this.circuitBreaker.failures = 0; this.circuitBreaker.isOpen = false; this.logger.info('Circuit breaker reset after successful operation'); } } /** * Main pattern recognition method * @param {object} intent - Intent analysis result * @param {object} options - Additional options for pattern matching * @returns {object} Pattern recognition result with wireframe and components */ async recognize(intent, options = {}) { const requestId = this.generateRequestId(); const startTime = performance.now(); try { // Check circuit breaker if (this.circuitBreaker.isOpen) { const now = Date.now(); const timeSinceLastFailure = now - this.circuitBreaker.lastFailure; if (timeSinceLastFailure > this.circuitBreaker.resetTimeout) { this.circuitBreaker.isOpen = false; this.circuitBreaker.failures = 0; this.logger.info('Circuit breaker reset - attempting recognition', { requestId }); } else { this.logger.warn('Circuit breaker is open - returning fallback result', { requestId, resetIn: this.circuitBreaker.resetTimeout - timeSinceLastFailure }); return this.createFallbackRecognition(intent, options, requestId); } } // Input validation this.validateInput(intent, options, requestId); // Step 1: Find matching patterns with error handling const matchedPatterns = await this.safeExecuteAsync(() => this.findPatterns(intent), 'pattern matching', requestId); if (!matchedPatterns || matchedPatterns.length === 0) { throw new PatternRecognitionError('No matching patterns found'); } // Step 2: Score and rank patterns const rankedPatterns = this.safeExecute(() => this.rankPatterns(matchedPatterns, intent), 'pattern ranking', requestId); // Step 3: Select best pattern const selectedPattern = rankedPatterns[0]; if (!selectedPattern) { throw new PatternRecognitionError('No suitable pattern selected'); } // Step 4: Generate wireframe with error handling const wireframe = await this.safeExecuteAsync(() => this.generateWireframe(selectedPattern, intent), 'wireframe generation', requestId); // Step 5: Optimize layout const optimizedLayout = await this.safeExecuteAsync(() => this.optimizeLayout(wireframe, intent), 'layout optimization', requestId); // Step 6: Suggest components const componentSuggestions = await this.safeExecuteAsync(() => this.suggestComponents(selectedPattern, intent), 'component suggestion', requestId); // Step 7: Calculate layout metrics const layoutMetrics = this.safeExecute(() => this.calculateLayoutMetrics(optimizedLayout), 'metrics calculation', requestId); const recognitionTime = performance.now() - startTime; // Update metrics and reset circuit breaker on success this.updateMetrics(selectedPattern.score, true); this.resetCircuitBreaker(); const result = { selectedPattern: { id: selectedPattern.id, name: selectedPattern.name, type: selectedPattern.type, score: selectedPattern.score, confidence: selectedPattern.confidence }, wireframe: { ascii: wireframe.ascii, structure: wireframe.structure, dimensions: wireframe.dimensions }, layout: { type: optimizedLayout.type, grid: optimizedLayout.grid, breakpoints: optimizedLayout.breakpoints, spacing: optimizedLayout.spacing }, components: componentSuggestions, layoutMetrics: layoutMetrics, alternatives: rankedPatterns.slice(1, 4), // Top 3 alternatives metadata: { recognitionTime: Math.round(recognitionTime), patternsEvaluated: matchedPatterns.length, timestamp: new Date().toISOString(), requestId } }; // Log successful recognition this.logger.logPatternRecognition(requestId, intent, result, recognitionTime); return result; } catch (error) { return await this.handleRecognitionError(error, requestId, intent, options, startTime); } } /** * Find patterns that match the given intent */ findPatterns(intent) { if (!this.patterns || !Array.isArray(this.patterns)) { throw new PatternRecognitionError('Patterns database not properly loaded'); } const matches = []; for (const pattern of this.patterns) { try { const score = this.calculatePatternScore(pattern, intent); if (score > 0) { matches.push({ ...pattern, score, confidence: Math.min(score / 10, 1.0) }); } } catch (error) { this.logger.warn('Error evaluating pattern', error, { patternId: pattern.id }); continue; } } return matches; } /** * Calculate how well a pattern matches an intent */ calculatePatternScore(pattern, intent) { if (!pattern || !intent || !intent.intent) { return 0; } let score = 0; try { // Primary intent match (50% of score) if (pattern.intents?.primary && Array.isArray(pattern.intents.primary) && pattern.intents.primary.includes(intent.intent.type)) { score += 5; } // Subtype match (20% of score) if (pattern.intents?.subtypes && Array.isArray(pattern.intents.subtypes) && intent.intent.subtype && pattern.intents.subtypes.includes(intent.intent.subtype)) { score += 2; } // Style compatibility (20% of score) if (intent.style?.primary && pattern.styles?.compatible && Array.isArray(pattern.styles.compatible) && pattern.styles.compatible.includes(intent.style.primary)) { score += 2; } // Component overlap (10% of score) if (pattern.components?.core && intent.components?.required) { const componentOverlap = this.calculateComponentOverlap( pattern.components.core, intent.components.required ); score += componentOverlap; } } catch (error) { this.logger.warn('Error calculating pattern score', error, { patternId: pattern.id, intentType: intent.intent?.type }); return 0; } return score; } /** * Calculate component overlap between pattern and intent */ calculateComponentOverlap(patternComponents, intentComponents) { if (!Array.isArray(patternComponents) || !Array.isArray(intentComponents)) { return 0; } try { const overlap = patternComponents.filter(c => intentComponents.includes(c)); return overlap.length / Math.max(patternComponents.length, intentComponents.length); } catch (error) { this.logger.warn('Error calculating component overlap', error); return 0; } } /** * Rank patterns by score and compatibility */ rankPatterns(patterns, intent) { if (!Array.isArray(patterns)) { return []; } return patterns .sort((a, b) => { try { // Primary sort by score if (b.score !== a.score) { return b.score - a.score; } // Secondary sort by style compatibility const aStyleMatch = intent.style?.primary && a.styles?.compatible?.includes(intent.style.primary); const bStyleMatch = intent.style?.primary && b.styles?.compatible?.includes(intent.style.primary); if (bStyleMatch && !aStyleMatch) return 1; if (aStyleMatch && !bStyleMatch) return -1; // Tertiary sort by complexity match return (a.complexity || 5) - (b.complexity || 5); } catch (error) { this.logger.warn('Error ranking patterns', error); return 0; } }); } /** * Generate ASCII wireframe for selected pattern */ generateWireframe(pattern, intent) { if (!pattern || !pattern.layout?.template) { throw new PatternRecognitionError('Invalid pattern or missing layout template'); } const template = this.layoutTemplates[pattern.layout.template]; if (!template) { throw new PatternRecognitionError(`Layout template '${pattern.layout.template}' not found`); } try { // Customize template based on intent const customizedTemplate = this.customizeTemplate(template, intent, pattern); // Generate ASCII representation const ascii = this.generateAsciiWireframe(customizedTemplate); // Calculate dimensions const dimensions = this.calculateDimensions(customizedTemplate); return { ascii, structure: customizedTemplate.structure, dimensions }; } catch (error) { this.logger.error('Error generating wireframe', error, { patternId: pattern.id, template: pattern.layout.template }); throw new PatternRecognitionError(`Failed to generate wireframe: ${error.message}`); } } /** * Customize template based on intent and pattern */ customizeTemplate(template, intent, pattern) { try { const customized = JSON.parse(JSON.stringify(template)); // Deep clone if (!customized.structure?.sections || !Array.isArray(customized.structure.sections)) { return customized; } // Customize sections based on intent for (const section of customized.structure.sections) { if (!section.components) { section.components = []; } if (section.type === 'header' && intent.components?.required?.includes('navigation')) { if (!section.components.includes('navigation')) { section.components.push('navigation'); } } if (section.type === 'main' && intent.intent?.type === 'dashboard') { ['charts', 'tables'].forEach(comp => { if (!section.components.includes(comp)) { section.components.push(comp); } }); } if (section.type === 'sidebar' && pattern.components?.core?.includes('sidebar')) { ['navigation', 'filters'].forEach(comp => { if (!section.components.includes(comp)) { section.components.push(comp); } }); } } return customized; } catch (error) { this.logger.warn('Error customizing template', error); return template; // Return original on error } } /** * Generate ASCII art wireframe */ generateAsciiWireframe(template) { try { const { width, height } = template.dimensions || { width: 40, height: 10 }; const sections = template.structure?.sections || []; let ascii = ''; // Top border ascii += '┌' + '─'.repeat(Math.max(width - 2, 1)) + '┐\n'; let currentRow = 1; for (let i = 0; i < sections.length; i++) { const section = sections[i]; const sectionHeight = Math.max(Math.floor((section.height || 20) / 100 * height), 1); const sectionLabel = this.getSectionLabel(section); // Section content if (section.type === 'header') { ascii += `│${this.centerText(sectionLabel, width - 2)}│\n`; ascii += '├' + '─'.repeat(width - 2) + '┤\n'; currentRow += 2; } else if (section.type === 'footer') { ascii += '├' + '─'.repeat(width - 2) + '┤\n'; ascii += `│${this.centerText(sectionLabel, width - 2)}│\n`; currentRow += 2; } else { // Main content sections for (let j = 0; j < Math.max(sectionHeight - 1, 1); j++) { if (j === 0) { ascii += `│${this.centerText(sectionLabel, width - 2)}│\n`; } else if (j === Math.floor(sectionHeight / 2) && section.components && section.components.length > 0) { const componentsText = section.components.join(', '); ascii += `│${this.centerText(componentsText, width - 2)}│\n`; } else { ascii += '│' + ' '.repeat(width - 2) + '│\n'; } currentRow++; } if (i !== sections.length - 1) { ascii += '├' + '─'.repeat(width - 2) + '┤\n'; currentRow++; } } } // Bottom border ascii += '└' + '─'.repeat(width - 2) + '┘'; return ascii; } catch (error) { this.logger.warn('Error generating ASCII wireframe', error); return 'Error generating wireframe'; } } /** * Get section label for display */ getSectionLabel(section) { const labels = { header: 'HEADER', main: 'MAIN CONTENT', sidebar: 'SIDEBAR', footer: 'FOOTER', hero: 'HERO SECTION', navigation: 'NAVIGATION', content: 'CONTENT AREA' }; return labels[section.type] || (section.type || 'SECTION').toUpperCase(); } /** * Center text within given width */ centerText(text, width) { if (!text) text = ''; if (width <= 0) return ''; if (text.length >= width) { return text.substring(0, width); } const padding = Math.floor((width - text.length) / 2); const leftPad = ' '.repeat(Math.max(padding, 0)); const rightPad = ' '.repeat(Math.max(width - text.length - padding, 0)); return leftPad + text + rightPad; } /** * Calculate wireframe dimensions */ calculateDimensions(template) { const width = template.dimensions?.width || 40; const height = template.dimensions?.height || 10; const sections = template.structure?.sections || []; return { width, height, aspectRatio: height > 0 ? width / height : 1, totalSections: sections.length }; } /** * Optimize layout using mathematical principles */ optimizeLayout(wireframe, intent) { try { const goldenRatio = 1.618; // Calculate optimal grid system const gridColumns = intent.constraints?.device === 'mobile' ? 4 : 12; const gridGutters = 16; // pixels // Calculate spacing system based on golden ratio const baseSpacing = 8; // pixels const spacingScale = [ baseSpacing, Math.round(baseSpacing * goldenRatio), Math.round(baseSpacing * Math.pow(goldenRatio, 2)), Math.round(baseSpacing * Math.pow(goldenRatio, 3)) ]; // Calculate breakpoints const breakpoints = { mobile: 375, tablet: 768, desktop: 1024, wide: 1440 }; return { type: 'css-grid', grid: { columns: gridColumns, gutters: gridGutters, rows: 'auto' }, breakpoints, spacing: { scale: spacingScale, unit: 'px' }, proportions: { goldenRatio, headerRatio: 1/8, footerRatio: 1/12, contentRatio: 3/4 } }; } catch (error) { this.logger.warn('Error optimizing layout', error); return this.getFallbackResult('layout optimization'); } } /** * Suggest components based on pattern and intent */ suggestComponents(pattern, intent) { try { const suggestions = { required: [...(pattern.components?.core || [])], recommended: [...(pattern.components?.optional || [])], alternative: [] }; // Add intent-specific components if (intent.components?.required) { for (const component of intent.components.required) { if (!suggestions.required.includes(component)) { suggestions.required.push(component); } } } // Add related components based on relationships for (const component of suggestions.required) { const related = this.componentRelationships[component]; if (related?.complements) { for (const relatedComponent of related.complements) { if (!suggestions.required.includes(relatedComponent) && !suggestions.recommended.includes(relatedComponent)) { suggestions.recommended.push(relatedComponent); } } } // Add alternatives if (related?.alternatives) { suggestions.alternative.push(...related.alternatives); } } return suggestions; } catch (error) { this.logger.warn('Error suggesting components', error); return this.getFallbackResult('component suggestion'); } } /** * Calculate layout metrics for optimization */ calculateLayoutMetrics(layout) { try { return { complexity: this.calculateComplexity(layout), accessibility: this.estimateAccessibility(layout), performance: this.estimatePerformance(layout), responsiveness: this.calculateResponsiveness(layout) }; } catch (error) { this.logger.warn('Error calculating layout metrics', error); return { complexity: 0.5, accessibility: 0.7, performance: 0.8, responsiveness: 0.6 }; } } /** * Calculate layout complexity score */ calculateComplexity(layout) { try { let complexity = 0; // Grid complexity const columns = layout.grid?.columns || 12; complexity += columns / 12; // Normalized to 12-column grid // Breakpoint complexity const breakpointCount = Object.keys(layout.breakpoints || {}).length; complexity += breakpointCount / 4; // Normalized to 4 breakpoints // Spacing complexity const spacingLevels = layout.spacing?.scale?.length || 4; complexity += spacingLevels / 4; // Normalized to 4 levels return Math.min(complexity / 3, 1.0); // Normalize to 0-1 } catch (error) { return 0.5; // Default complexity } } /** * Estimate accessibility score */ estimateAccessibility(layout) { try { let score = 0.8; // Base score // Grid-based layouts are generally more accessible if (layout.type === 'css-grid') score += 0.1; // Responsive design improves accessibility const breakpointCount = Object.keys(layout.breakpoints || {}).length; if (breakpointCount >= 3) score += 0.1; return Math.min(score, 1.0); } catch (error) { return 0.7; // Default accessibility } } /** * Estimate performance score */ estimatePerformance(layout) { try { let score = 0.7; // Base score // CSS Grid is more performant than complex flexbox if (layout.type === 'css-grid') score += 0.2; // Fewer breakpoints = better performance const breakpointCount = Object.keys(layout.breakpoints || {}).length; const breakpointPenalty = Math.max(breakpointCount - 3, 0) * 0.05; score -= breakpointPenalty; return Math.max(Math.min(score, 1.0), 0); } catch (error) { return 0.8; // Default performance } } /** * Calculate responsiveness score */ calculateResponsiveness(layout) { try { const breakpointCount = Object.keys(layout.breakpoints || {}).length; return Math.min(breakpointCount / 4, 1.0); // Normalized to 4 breakpoints max } catch (error) { return 0.5; // Default responsiveness } } /** * Load UI patterns database */ loadPatterns() { return [ { id: 'dashboard-analytics', name: 'Analytics Dashboard', type: 'dashboard', intents: { primary: ['dashboard'], subtypes: ['analytics', 'metrics', 'data'] }, styles: { compatible: ['modern', 'minimalist', 'professional', 'dark'] }, components: { core: ['navigation', 'sidebar', 'card', 'chart'], optional: ['table', 'search', 'filter'] }, layout: { template: 'dashboard-sidebar' }, complexity: 7 }, { id: 'landing-saas', name: 'SaaS Landing Page', type: 'landing', intents: { primary: ['landing'], subtypes: ['saas', 'startup', 'product'] }, styles: { compatible: ['modern', 'minimalist', 'professional', 'glassmorphism'] }, components: { core: ['navigation', 'hero', 'footer'], optional: ['card', 'button', 'form', 'testimonials'] }, layout: { template: 'landing-hero' }, complexity: 5 }, { id: 'auth-login', name: 'Login Form', type: 'auth', intents: { primary: ['auth'], subtypes: ['login', 'signin'] }, styles: { compatible: ['modern', 'minimalist', 'glassmorphism', 'dark', 'light'] }, components: { core: ['form', 'button'], optional: ['card', 'modal'] }, layout: { template: 'centered-card' }, complexity: 3 }, { id: 'ecommerce-product', name: 'E-commerce Product Page', type: 'ecommerce', intents: { primary: ['ecommerce', 'shop'], subtypes: ['product', 'catalog'] }, styles: { compatible: ['modern', 'minimalist', 'professional'] }, components: { core: ['navigation', 'card', 'button', 'search'], optional: ['filter', 'pagination', 'modal', 'carousel'] }, layout: { template: 'product-grid' }, complexity: 6 }, { id: 'portfolio-gallery', name: 'Portfolio Gallery', type: 'portfolio', intents: { primary: ['portfolio'], subtypes: ['gallery', 'showcase'] }, styles: { compatible: ['modern', 'minimalist', 'creative', 'dark', 'light'] }, components: { core: ['navigation', 'card', 'footer'], optional: ['carousel', 'modal', 'tabs', 'filter'] }, layout: { template: 'masonry-grid' }, complexity: 5 } ]; } /** * Load layout templates */ loadLayoutTemplates() { return { 'dashboard-sidebar': { dimensions: { width: 60, height: 20 }, structure: { sections: [ { type: 'header', height: 10, components: ['navigation', 'user-menu'] }, { type: 'main', height: 80, components: ['sidebar', 'content'], layout: 'side-by-side' }, { type: 'footer', height: 10, components: ['copyright'] } ] } }, 'landing-hero': { dimensions: { width: 60, height: 25 }, structure: { sections: [ { type: 'header', height: 12, components: ['navigation'] }, { type: 'hero', height: 40, components: ['hero-text', 'cta-button'] }, { type: 'content', height: 40, components: ['features', 'testimonials'] }, { type: 'footer', height: 8, components: ['links', 'copyright'] } ] } }, 'centered-card': { dimensions: { width: 50, height: 15 }, structure: { sections: [ { type: 'main', height: 100, components: ['form-card'], layout: 'centered' } ] } }, 'product-grid': { dimensions: { width: 60, height: 22 }, structure: { sections: [ { type: 'header', height: 12, components: ['navigation', 'search'] }, { type: 'main', height: 80, components: ['filters', 'product-grid'], layout: 'side-by-side' }, { type: 'footer', height: 8, components: ['pagination', 'links'] } ] } }, 'masonry-grid': { dimensions: { width: 60, height: 20 }, structure: { sections: [ { type: 'header', height: 15, components: ['navigation', 'hero'] }, { type: 'main', height: 75, components: ['masonry-gallery'] }, { type: 'footer', height: 10, components: ['contact', 'social'] } ] } } }; } /** * Load component relationships */ loadComponentRelationships() { return { navigation: { complements: ['logo', 'search', 'user-menu'], alternatives: ['breadcrumb', 'tabs'] }, sidebar: { complements: ['navigation', 'filter', 'search'], alternatives: ['drawer', 'collapsible-menu'] }, card: { complements: ['button', 'image', 'text'], alternatives: ['tile', 'panel', 'item'] }, chart: { complements: ['legend', 'filter', 'export'], alternatives: ['table', 'graph', 'visualization'] }, form: { complements: ['button', 'validation', 'label'], alternatives: ['modal-form', 'wizard', 'inline-edit'] }, table: { complements: ['pagination', 'search', 'sort'], alternatives: ['grid', 'list', 'cards'] } }; } /** * Update performance metrics */ updateMetrics(score, success = true) { try { this.metrics.totalRecognitions++; if (success && score >= 5) { this.metrics.successfulMatches++; } // Calculate running average of match score const totalScore = this.metrics.averageMatchScore * (this.metrics.totalRecognitions - 1) + score; this.metrics.averageMatchScore = totalScore / this.metrics.totalRecognitions; } catch (error) { this.logger.warn('Error updating metrics', error); } } /** * Get performance metrics */ getMetrics() { return { ...this.metrics, successRate: this.metrics.totalRecognitions > 0 ? this.metrics.successfulMatches / this.metrics.totalRecognitions : 0, recoveryRate: this.metrics.errors > 0 ? this.metrics.recoveredErrors / this.metrics.errors : 0, circuitBreakerStatus: { isOpen: this.circuitBreaker.isOpen, failures: this.circuitBreaker.failures, threshold: this.circuitBreaker.threshold } }; } /** * Reset metrics and circuit breaker */ reset() { this.metrics = { totalRecognitions: 0, successfulMatches: 0, averageMatchScore: 0, errors: 0, recoveredErrors: 0 }; this.circuitBreaker = { isOpen: false, failures: 0, lastFailure: null, threshold: this.circuitBreaker.threshold, resetTimeout: this.circuitBreaker.resetTimeout }; this.logger.info('Pattern Recognition Engine reset'); } } module.exports = { PatternRecognitionEngine, PatternRecognitionError }; // Example usage and testing if (require.main === module) { const { IntentAnalysisEngine } = require('../intent/IntentAnalysisEngine'); async function test() { const intentEngine = new IntentAnalysisEngine(); const patternEngine = new PatternRecognitionEngine(); const testCases = [ "Design a modern dashboard for project management", "Create a landing page for a SaaS startup", "Build a dark theme login page with glassmorphism effects", null, // Test null input { invalid: 'intent' }, // Test invalid intent "a" // Test minimal input ]; console.log('🎯 Testing Pattern Recognition Engine with Error Handling...\n'); for (const testCase of testCases) { try { console.log(`Input: "${testCase}"`); // First analyze intent const intent = await intentEngine.analyze(testCase); // Then recognize patterns const pattern = await patternEngine.recognize(intent); console.log(`Selected Pattern: ${pattern.selectedPattern.name}`); console.log(`Confidence: ${Math.round(pattern.selectedPattern.confidence * 100)}%`); console.log(`Components: ${pattern.components.required.join(', ')}`); console.log('Wireframe:'); console.log(pattern.wireframe.ascii); if (pattern.metadata.fallbackUsed) { console.log('⚠️ Fallback pattern used'); } console.log('---\n'); } catch (error) { console.error(`❌ Error: ${error.message}`); console.log('---\n'); } } const metrics = patternEngine.getMetrics(); console.log(`📊 Performance Metrics:`); console.log(`Total Recognitions: ${metrics.totalRecognitions}`); console.log(`Success Rate: ${Math.round(metrics.successRate * 100)}%`); console.log(`Average Match Score: ${Math.round(metrics.averageMatchScore * 10) / 10}`); console.log(`Errors: ${metrics.errors}`); console.log(`Recovery Rate: ${Math.round(metrics.recoveryRate * 100)}%`); console.log(`Circuit Breaker: ${metrics.circuitBreakerStatus.isOpen ? 'Open' : 'Closed'}`); } test().catch(console.error); }