Skip to main content
Glama
ai-generator.js•29.3 kB
// AI-Powered Code Generator export class AIGenerator { constructor() { this.componentTemplates = new Map(); this.initializeTemplates(); } // Simple UI Resource creator to replace @mcp-ui/server dependency createUIResource({ uri, content, encoding = 'text' }) { return { uri, mimeType: 'text/html', text: content.htmlString, encoding }; } // Initialize component templates initializeTemplates() { this.componentTemplates.set('form', { description: 'A form component with customizable fields', generate: this.generateFormFromDescription.bind(this) }); this.componentTemplates.set('dashboard', { description: 'A dashboard with widgets and metrics', generate: this.generateDashboardFromDescription.bind(this) }); this.componentTemplates.set('chart', { description: 'A data visualization chart', generate: this.generateChartFromDescription.bind(this) }); this.componentTemplates.set('custom', { description: 'A custom HTML component', generate: this.generateCustomFromDescription.bind(this) }); } // Parse natural language description and extract component requirements parseDescription(description) { const requirements = { type: 'form', title: 'Generated Component', fields: [], widgets: [], data: {}, styling: {}, interactions: [] }; const lowerDesc = description.toLowerCase(); // Determine component type if (lowerDesc.includes('form') || lowerDesc.includes('input') || lowerDesc.includes('submit')) { requirements.type = 'form'; this.extractFormRequirements(description, requirements); } else if (lowerDesc.includes('dashboard') || lowerDesc.includes('widget') || lowerDesc.includes('metric')) { requirements.type = 'dashboard'; this.extractDashboardRequirements(description, requirements); } else if (lowerDesc.includes('chart') || lowerDesc.includes('graph') || lowerDesc.includes('visualization')) { requirements.type = 'chart'; this.extractChartRequirements(description, requirements); } else { requirements.type = 'custom'; this.extractCustomRequirements(description, requirements); } return requirements; } // Extract form requirements from description extractFormRequirements(description, requirements) { const lowerDesc = description.toLowerCase(); // Extract title const titleMatch = description.match(/title[:\s]+([^,.\n]+)/i); if (titleMatch) { requirements.title = titleMatch[1].trim(); } // Extract fields based on keywords const fieldKeywords = { 'name': { type: 'text', label: 'Full Name', required: true }, 'email': { type: 'email', label: 'Email Address', required: true }, 'phone': { type: 'tel', label: 'Phone Number', required: false }, 'message': { type: 'textarea', label: 'Message', required: false }, 'age': { type: 'number', label: 'Age', required: false }, 'gender': { type: 'select', label: 'Gender', options: ['Male', 'Female', 'Other'], required: false }, 'country': { type: 'select', label: 'Country', options: ['USA', 'Canada', 'UK', 'Other'], required: false }, 'subject': { type: 'text', label: 'Subject', required: true }, 'company': { type: 'text', label: 'Company', required: false }, 'website': { type: 'url', label: 'Website', required: false } }; Object.entries(fieldKeywords).forEach(([keyword, fieldConfig]) => { if (lowerDesc.includes(keyword)) { requirements.fields.push({ name: keyword, ...fieldConfig }); } }); // If no specific fields found, add default fields if (requirements.fields.length === 0) { requirements.fields = [ { name: 'name', type: 'text', label: 'Full Name', required: true }, { name: 'email', type: 'email', label: 'Email Address', required: true }, { name: 'message', type: 'textarea', label: 'Message', required: false } ]; } } // Extract dashboard requirements from description extractDashboardRequirements(description, requirements) { const lowerDesc = description.toLowerCase(); // Extract title const titleMatch = description.match(/title[:\s]+([^,.\n]+)/i); if (titleMatch) { requirements.title = titleMatch[1].trim(); } // Extract widgets based on keywords const widgetKeywords = { 'user': { type: 'metric', title: 'Total Users', data: { value: '1,234', label: 'Active users' } }, 'sales': { type: 'metric', title: 'Total Sales', data: { value: '$45,678', label: 'This month' } }, 'revenue': { type: 'metric', title: 'Revenue', data: { value: '$12,345', label: 'This week' } }, 'activity': { type: 'list', title: 'Recent Activities', data: { items: ['User login', 'Data update', 'Report generated'] } }, 'chart': { type: 'chart', title: 'Sales Chart', data: { values: [100, 150, 200, 175], labels: ['Q1', 'Q2', 'Q3', 'Q4'] } } }; Object.entries(widgetKeywords).forEach(([keyword, widgetConfig]) => { if (lowerDesc.includes(keyword)) { requirements.widgets.push(widgetConfig); } }); // If no specific widgets found, add default widgets if (requirements.widgets.length === 0) { requirements.widgets = [ { type: 'metric', title: 'Total Users', data: { value: '1,234', label: 'Active users' } }, { type: 'list', title: 'Recent Activities', data: { items: ['User login', 'Data update', 'Report generated'] } } ]; } } // Extract chart requirements from description extractChartRequirements(description, requirements) { const lowerDesc = description.toLowerCase(); // Extract title const titleMatch = description.match(/title[:\s]+([^,.\n]+)/i); if (titleMatch) { requirements.title = titleMatch[1].trim(); } // Determine chart type if (lowerDesc.includes('pie') || lowerDesc.includes('circle')) { requirements.data.type = 'pie'; } else { requirements.data.type = 'bar'; } // Extract data based on keywords if (lowerDesc.includes('sales') || lowerDesc.includes('revenue')) { requirements.data.values = [120, 150, 180, 200, 175]; requirements.data.labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May']; } else if (lowerDesc.includes('user') || lowerDesc.includes('traffic')) { requirements.data.values = [100, 120, 140, 160, 180]; requirements.data.labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']; } else { requirements.data.values = [50, 75, 100, 125, 150]; requirements.data.labels = ['Q1', 'Q2', 'Q3', 'Q4', 'Q5']; } } // Extract custom component requirements extractCustomRequirements(description, requirements) { const lowerDesc = description.toLowerCase(); // Extract title const titleMatch = description.match(/title[:\s]+([^,.\n]+)/i); if (titleMatch) { requirements.title = titleMatch[1].trim(); } // Generate custom HTML based on description requirements.html = this.generateCustomHTML(description); } // Generate custom HTML based on description generateCustomHTML(description) { const lowerDesc = description.toLowerCase(); if (lowerDesc.includes('card') || lowerDesc.includes('profile')) { return ` <div style=" padding: 2rem; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.2); max-width: 400px; margin: 0 auto; "> <div style="text-align: center;"> <div style=" width: 80px; height: 80px; border-radius: 50%; background: rgba(255,255,255,0.2); margin: 0 auto 1rem; display: flex; align-items: center; justify-content: center; font-size: 2rem; ">👤</div> <h2 style="margin-bottom: 0.5rem;">John Doe</h2> <p style="margin-bottom: 1rem; opacity: 0.8;">Software Developer</p> <div style=" display: flex; justify-content: space-around; margin-top: 1.5rem; "> <div style="text-align: center;"> <div style="font-size: 1.5rem; font-weight: bold;">150</div> <div style="font-size: 0.9rem; opacity: 0.8;">Projects</div> </div> <div style="text-align: center;"> <div style="font-size: 1.5rem; font-weight: bold;">5+</div> <div style="font-size: 0.9rem; opacity: 0.8;">Years</div> </div> <div style="text-align: center;"> <div style="font-size: 1.5rem; font-weight: bold;">4.9</div> <div style="font-size: 0.9rem; opacity: 0.8;">Rating</div> </div> </div> </div> </div> `; } else if (lowerDesc.includes('button') || lowerDesc.includes('cta')) { return ` <div style=" padding: 2rem; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.2); text-align: center; "> <h2 style="margin-bottom: 1rem;">Call to Action</h2> <p style="margin-bottom: 2rem; opacity: 0.9;">Ready to get started? Click the button below!</p> <button onclick="alert('Button clicked!')" style=" padding: 1rem 2rem; border: none; border-radius: 8px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; font-size: 1.1rem; font-weight: 600; cursor: pointer; transition: all 0.2s; box-shadow: 0 4px 12px rgba(0,0,0,0.2); ">Get Started</button> </div> `; } else { return ` <div style=" padding: 2rem; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.2); text-align: center; "> <h2 style="margin-bottom: 1rem;">Custom Component</h2> <p style="margin-bottom: 1rem; opacity: 0.9;">This is a custom component generated based on your description.</p> <div style=" display: flex; justify-content: center; gap: 1rem; margin-top: 1.5rem; "> <button onclick="alert('Action 1')" style=" padding: 0.75rem 1.5rem; border: none; border-radius: 6px; background: rgba(255,255,255,0.2); color: white; cursor: pointer; ">Action 1</button> <button onclick="alert('Action 2')" style=" padding: 0.75rem 1.5rem; border: none; border-radius: 6px; background: rgba(255,255,255,0.2); color: white; cursor: pointer; ">Action 2</button> </div> </div> `; } } // Generate component from AI description async generateFromDescription(userId, description) { try { console.log('AI Generating component from description:', description); const requirements = this.parseDescription(description); const componentId = `ai-${userId}-${Date.now()}`; let resource; switch (requirements.type) { case 'form': resource = await this.generateFormFromDescription(requirements, componentId); break; case 'dashboard': resource = await this.generateDashboardFromDescription(requirements, componentId); break; case 'chart': resource = await this.generateChartFromDescription(requirements, componentId); break; case 'custom': resource = await this.generateCustomFromDescription(requirements, componentId); break; default: throw new Error('Unknown component type'); } return resource; } catch (error) { console.error('AI Generation error:', error); throw error; } } // Generate form from AI requirements async generateFormFromDescription(requirements, componentId) { const formConfig = { title: requirements.title, fields: requirements.fields, submitText: 'Submit' }; const formHTML = this.createFormHTML(formConfig, componentId); const resource = this.createUIResource({ uri: `ui://ai/form/${componentId}`, content: { type: 'rawHtml', htmlString: formHTML }, encoding: 'text' }); return resource; } // Generate dashboard from AI requirements async generateDashboardFromDescription(requirements, componentId) { const dashboardConfig = { title: requirements.title, widgets: requirements.widgets }; const dashboardHTML = this.createDashboardHTML(dashboardConfig, componentId); const resource = this.createUIResource({ uri: `ui://ai/dashboard/${componentId}`, content: { type: 'rawHtml', htmlString: dashboardHTML }, encoding: 'text' }); return resource; } // Generate chart from AI requirements async generateChartFromDescription(requirements, componentId) { const chartConfig = { title: requirements.title, type: requirements.data.type, data: requirements.data }; const chartHTML = this.createChartHTML(chartConfig, componentId); const resource = this.createUIResource({ uri: `ui://ai/chart/${componentId}`, content: { type: 'rawHtml', htmlString: chartHTML }, encoding: 'text' }); return resource; } // Generate custom component from AI requirements async generateCustomFromDescription(requirements, componentId) { const resource = this.createUIResource({ uri: `ui://ai/custom/${componentId}`, content: { type: 'rawHtml', htmlString: requirements.html }, encoding: 'text' }); return resource; } // Get suggestions based on description getSuggestions(description) { const suggestions = []; const lowerDesc = description.toLowerCase(); if (lowerDesc.includes('form') || lowerDesc.includes('input')) { suggestions.push('Contact form with name, email, and message fields'); suggestions.push('Registration form with username, password, and confirm password'); suggestions.push('Survey form with multiple choice questions'); } if (lowerDesc.includes('dashboard') || lowerDesc.includes('widget')) { suggestions.push('Analytics dashboard with user metrics and charts'); suggestions.push('Sales dashboard with revenue and conversion metrics'); suggestions.push('Project management dashboard with tasks and progress'); } if (lowerDesc.includes('chart') || lowerDesc.includes('graph')) { suggestions.push('Bar chart showing monthly sales data'); suggestions.push('Pie chart displaying user demographics'); suggestions.push('Line chart tracking website traffic over time'); } if (lowerDesc.includes('card') || lowerDesc.includes('profile')) { suggestions.push('User profile card with avatar and stats'); suggestions.push('Product card with image, title, and price'); suggestions.push('Team member card with role and contact info'); } return suggestions; } // Create form HTML (reuse from MCP server) createFormHTML(config, formId) { const { title, fields, submitText = 'Submit' } = config; const fieldsHTML = fields.map(field => { const { name, type, label, placeholder, required, options } = field; if (type === 'select') { const optionsHTML = options.map(opt => `<option value="${opt.value}">${opt.label}</option>` ).join(''); return ` <div class="form-field"> <label for="${name}">${label}${required ? ' *' : ''}</label> <select id="${name}" name="${name}" ${required ? 'required' : ''}> <option value="">Select ${label}</option> ${optionsHTML} </select> </div> `; } else if (type === 'textarea') { return ` <div class="form-field"> <label for="${name}">${label}${required ? ' *' : ''}</label> <textarea id="${name}" name="${name}" placeholder="${placeholder || ''}" ${required ? 'required' : ''}></textarea> </div> `; } else { return ` <div class="form-field"> <label for="${name}">${label}${required ? ' *' : ''}</label> <input type="${type}" id="${name}" name="${name}" placeholder="${placeholder || ''}" ${required ? 'required' : ''}> </div> `; } }).join(''); return ` <div class="ai-generated-form" style=" padding: 2rem; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.2); max-width: 600px; margin: 0 auto; "> <h2 style="margin-bottom: 1.5rem; text-align: center;">${title}</h2> <form id="${formId}" onsubmit="handleFormSubmit(event, '${formId}')"> ${fieldsHTML} <div style="text-align: center; margin-top: 2rem;"> <button type="submit" style=" padding: 1rem 2rem; border: none; border-radius: 8px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; font-size: 1.1rem; font-weight: 600; cursor: pointer; transition: all 0.2s; box-shadow: 0 4px 12px rgba(0,0,0,0.2); ">${submitText}</button> </div> </form> <script> function handleFormSubmit(event, formId) { event.preventDefault(); const form = event.target; const formData = new FormData(form); const data = {}; for (let [key, value] of formData.entries()) { data[key] = value; } window.parent.postMessage({ type: 'form-submit', payload: { formId: formId, data: data } }, '*'); alert('Form submitted successfully!'); } </script> <style> .form-field { margin-bottom: 1.5rem; } .form-field label { display: block; margin-bottom: 0.5rem; font-weight: 500; } .form-field input, .form-field select, .form-field textarea { width: 100%; padding: 0.75rem; border: 2px solid rgba(255,255,255,0.3); border-radius: 8px; background: rgba(255,255,255,0.1); color: white; font-size: 1rem; transition: border-color 0.2s; } .form-field input:focus, .form-field select:focus, .form-field textarea:focus { outline: none; border-color: #10b981; } .form-field textarea { min-height: 100px; resize: vertical; } </style> </div> `; } // Create dashboard HTML (reuse from MCP server) createDashboardHTML(config, dashboardId) { const { title, widgets } = config; const widgetsHTML = widgets.map(widget => { const { type, title, data, size = 'medium' } = widget; if (type === 'metric') { return ` <div class="widget metric-widget" style=" background: rgba(255,255,255,0.1); padding: 1.5rem; border-radius: 8px; text-align: center; border: 1px solid rgba(255,255,255,0.2); "> <h3 style="margin-bottom: 0.5rem; font-size: 1.2rem;">${title}</h3> <div style="font-size: 2rem; font-weight: bold; color: #10b981;">${data.value}</div> <div style="font-size: 0.9rem; opacity: 0.8;">${data.label}</div> </div> `; } else if (type === 'list') { const itemsHTML = data.items.map(item => `<li style="padding: 0.5rem 0; border-bottom: 1px solid rgba(255,255,255,0.1);">${item}</li>` ).join(''); return ` <div class="widget list-widget" style=" background: rgba(255,255,255,0.1); padding: 1.5rem; border-radius: 8px; border: 1px solid rgba(255,255,255,0.2); "> <h3 style="margin-bottom: 1rem; font-size: 1.2rem;">${title}</h3> <ul style="list-style: none; padding: 0; margin: 0;"> ${itemsHTML} </ul> </div> `; } else if (type === 'chart') { let chartData = data; if (data.value && data.label && !data.values) { chartData = { values: [parseInt(data.value.replace(/,/g, '')) || 0], labels: [data.label] }; } if (!chartData.values || !chartData.labels) { chartData = { values: [0], labels: ['No Data'] }; } return ` <div class="widget chart-widget" style=" background: rgba(255,255,255,0.1); padding: 1.5rem; border-radius: 8px; border: 1px solid rgba(255,255,255,0.2); "> <h3 style="margin-bottom: 1rem; font-size: 1.2rem;">${title}</h3> <div style="height: 200px; display: flex; align-items: end; justify-content: space-around; padding: 1rem;"> ${chartData.values.map((value, index) => ` <div style=" background: linear-gradient(135deg, #10b981 0%, #059669 100%); width: 30px; height: ${(value / Math.max(...chartData.values)) * 150}px; border-radius: 4px; display: flex; align-items: end; justify-content: center; color: white; font-size: 0.8rem; font-weight: bold; ">${value}</div> `).join('')} </div> <div style="display: flex; justify-content: space-around; margin-top: 0.5rem;"> ${chartData.labels.map(label => ` <div style="font-size: 0.8rem; opacity: 0.8;">${label}</div> `).join('')} </div> </div> `; } }).join(''); return ` <div class="ai-generated-dashboard" style=" padding: 2rem; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.2); max-width: 800px; margin: 0 auto; "> <h2 style="margin-bottom: 2rem; text-align: center;">${title}</h2> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem;"> ${widgetsHTML} </div> <div style="text-align: center; margin-top: 2rem;"> <button onclick="refreshDashboard('${dashboardId}')" style=" padding: 0.75rem 1.5rem; border: none; border-radius: 8px; background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); color: white; font-size: 1rem; font-weight: 500; cursor: pointer; transition: all 0.2s; ">Refresh Dashboard</button> </div> <script> function refreshDashboard(dashboardId) { window.parent.postMessage({ type: 'dashboard-refresh', payload: { dashboardId: dashboardId } }, '*'); } </script> </div> `; } // Create chart HTML (reuse from MCP server) createChartHTML(config, chartId) { const { title, type, data, options = {} } = config; let chartHTML = ''; if (type === 'bar') { const maxValue = Math.max(...data.values); chartHTML = ` <div style="height: 300px; display: flex; align-items: end; justify-content: space-around; padding: 1rem;"> ${data.values.map((value, index) => ` <div style=" background: linear-gradient(135deg, #10b981 0%, #059669 100%); width: 40px; height: ${(value / maxValue) * 200}px; border-radius: 4px; display: flex; align-items: end; justify-content: center; color: white; font-size: 0.9rem; font-weight: bold; margin: 0 5px; ">${value}</div> `).join('')} </div> <div style="display: flex; justify-content: space-around; margin-top: 1rem;"> ${data.labels.map(label => ` <div style="font-size: 0.9rem; opacity: 0.8; text-align: center;">${label}</div> `).join('')} </div> `; } else if (type === 'pie') { const total = data.values.reduce((sum, val) => sum + val, 0); chartHTML = ` <div style="display: flex; align-items: center; justify-content: center; height: 300px;"> <div style=" width: 200px; height: 200px; border-radius: 50%; background: conic-gradient( ${data.values.map((value, index) => { const percentage = (value / total) * 100; const colors = ['#10b981', '#3b82f6', '#f59e0b', '#ef4444', '#8b5cf6']; return `${colors[index % colors.length]} ${index === 0 ? 0 : data.values.slice(0, index).reduce((sum, val) => sum + (val / total) * 100, 0)}% ${(value / total) * 100 + (index === 0 ? 0 : data.values.slice(0, index).reduce((sum, val) => sum + (val / total) * 100, 0))}%`; }).join(', ')} ); "></div> </div> <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem; margin-top: 1rem;"> ${data.labels.map((label, index) => { const colors = ['#10b981', '#3b82f6', '#f59e0b', '#ef4444', '#8b5cf6']; return ` <div style="display: flex; align-items: center; gap: 0.5rem;"> <div style=" width: 12px; height: 12px; border-radius: 2px; background: ${colors[index % colors.length]}; "></div> <span style="font-size: 0.9rem;">${label}</span> </div> `; }).join('')} </div> `; } return ` <div class="ai-generated-chart" style=" padding: 2rem; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.2); max-width: 600px; margin: 0 auto; "> <h2 style="margin-bottom: 1.5rem; text-align: center;">${title}</h2> ${chartHTML} <div style="text-align: center; margin-top: 2rem;"> <button onclick="exportChart('${chartId}')" style=" padding: 0.75rem 1.5rem; border: none; border-radius: 8px; background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: white; font-size: 1rem; font-weight: 500; cursor: pointer; transition: all 0.2s; ">Export Chart</button> </div> <script> function exportChart(chartId) { window.parent.postMessage({ type: 'chart-export', payload: { chartId: chartId } }, '*'); } </script> </div> `; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iamadi11/mcp-ui-poc'

If you have feedback or need assistance with the MCP directory API, please join our Discord server