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
JavaScript
/**
* 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);
}