Skip to main content
Glama
create-gradient-html.ts14.4 kB
/** * MCP tool for creating gradient preview HTML visualizations with CSS code display */ import { ToolHandler, ToolResponse, ErrorResponse } from '../types/index'; import { GradientVisualizationData } from '../visualization/html-generator'; import { enhancedHTMLGenerator, EnhancedVisualizationResult, EnhancedHTMLOptions, } from '../visualization/enhanced-html-generator'; import { enhancedFileOutputManager } from '../utils/enhanced-file-output-manager'; import { logger } from '../utils/logger'; import Joi from 'joi'; // Parameter validation schema const createGradientHtmlSchema = Joi.object({ gradient_css: Joi.string().required().description('CSS gradient definition'), preview_shapes: Joi.array() .items(Joi.string().valid('rectangle', 'circle', 'text', 'button', 'card')) .default(['rectangle']) .description('Preview shapes to show'), size: Joi.array() .items(Joi.number().integer().min(100).max(2000)) .length(2) .default([400, 300]) .description('Preview size [width, height]'), show_css_code: Joi.boolean().default(true).description('Display CSS code'), interactive_controls: Joi.boolean() .default(false) .description('Enable interactive controls'), variations: Joi.boolean() .default(false) .description('Show angle/position variations'), theme: Joi.string() .valid('light', 'dark', 'auto') .default('light') .description('Color theme for the visualization'), enable_background_controls: Joi.boolean() .default(true) .description('Enable interactive background controls'), enable_accessibility_testing: Joi.boolean() .default(true) .description('Enable accessibility testing and warnings'), include_keyboard_help: Joi.boolean() .default(true) .description('Include keyboard shortcuts help'), }); interface CreateGradientHtmlParams { gradient_css: string; preview_shapes?: string[]; size?: [number, number]; show_css_code?: boolean; interactive_controls?: boolean; variations?: boolean; theme?: 'light' | 'dark' | 'auto'; enable_background_controls?: boolean; enable_accessibility_testing?: boolean; include_keyboard_help?: boolean; } async function createGradientHtml( params: CreateGradientHtmlParams ): Promise<ToolResponse | ErrorResponse> { const startTime = Date.now(); try { // Validate parameters const { error, value } = createGradientHtmlSchema.validate(params); if (error) { return { success: false, error: { code: 'INVALID_PARAMETERS', message: 'Invalid parameters provided', details: error.details, suggestions: [ 'Ensure gradient_css is a valid CSS gradient string', 'Check that preview_shapes contains valid shape names', 'Verify size is an array of two numbers [width, height]', ], }, metadata: { execution_time: Date.now() - startTime, tool: 'create_gradient_html', timestamp: new Date().toISOString(), }, }; } const validatedParams = value as CreateGradientHtmlParams; // Validate CSS gradient syntax const gradientValidation = validateGradientCSS( validatedParams.gradient_css ); if (!gradientValidation.isValid) { return { success: false, error: { code: 'INVALID_GRADIENT_CSS', message: 'Invalid CSS gradient syntax', details: { gradient: validatedParams.gradient_css, reason: gradientValidation.error, }, suggestions: [ 'Use valid CSS gradient syntax like "linear-gradient(45deg, #ff0000, #0000ff)"', 'Check for proper color format in gradient stops', 'Ensure gradient type is supported (linear-gradient, radial-gradient, conic-gradient)', ], }, metadata: { execution_time: Date.now() - startTime, tool: 'create_gradient_html', timestamp: new Date().toISOString(), }, }; } const accessibilityNotes: string[] = []; const recommendations: string[] = []; // Analyze gradient for accessibility and recommendations const gradientAnalysis = analyzeGradient(validatedParams.gradient_css); if (gradientAnalysis.colorCount > 5) { recommendations.push( 'Consider using fewer colors for smoother gradient transitions' ); } if (gradientAnalysis.hasLowContrast) { accessibilityNotes.push( 'Some color combinations in the gradient may have low contrast' ); } if ( validatedParams.preview_shapes && validatedParams.preview_shapes.length > 4 ) { recommendations.push( 'Consider using fewer preview shapes for better performance' ); } // Determine gradient type for metadata const gradientType = determineGradientType(validatedParams.gradient_css); // Prepare visualization data const visualizationData: GradientVisualizationData = { gradientCSS: validatedParams.gradient_css, previewShapes: validatedParams.preview_shapes || ['rectangle'], size: validatedParams.size || [400, 300], showCSSCode: validatedParams.show_css_code !== false, interactiveControls: validatedParams.interactive_controls || false, variations: validatedParams.variations || false, metadata: { title: 'Gradient Preview', description: `${gradientType} gradient with ${gradientAnalysis.colorCount} colors`, timestamp: new Date().toLocaleString(), gradientType, }, }; // Generate enhanced HTML with background controls let result: EnhancedVisualizationResult; try { const enhancedOptions: EnhancedHTMLOptions = { interactive: true, // Enable interactive features including JavaScript backgroundControls: { enableToggle: validatedParams.enable_background_controls ?? true, enableColorPicker: validatedParams.enable_background_controls ?? true, defaultBackground: validatedParams.theme === 'dark' ? 'dark' : 'light', }, enableAccessibilityTesting: validatedParams.enable_accessibility_testing ?? true, includeKeyboardHelp: validatedParams.include_keyboard_help ?? true, }; result = await enhancedHTMLGenerator.generateEnhancedGradientHTML( visualizationData, enhancedOptions ); } catch (htmlError) { throw new Error( `Failed to generate enhanced HTML: ${htmlError instanceof Error ? htmlError.message : String(htmlError)}` ); } // Prepare export formats const exportFormats: Record<string, string | object> = {}; exportFormats['css'] = generateGradientCSSExport( validatedParams.gradient_css, gradientType ); exportFormats['scss'] = generateGradientSCSSExport( validatedParams.gradient_css, gradientType ); exportFormats['json'] = { gradient_css: validatedParams.gradient_css, gradient_type: gradientType, color_count: gradientAnalysis.colorCount, preview_shapes: validatedParams.preview_shapes, size: validatedParams.size, metadata: visualizationData.metadata, }; const executionTime = Date.now() - startTime; // Try to save file using enhanced file output manager let visualizationResult; try { await enhancedFileOutputManager.initialize(); // If we have HTML content, save it using the enhanced file output manager if (result.htmlContent) { visualizationResult = await enhancedFileOutputManager.saveHTMLVisualization( result.htmlContent, { toolName: 'create_gradient_html', description: `Enhanced gradient visualization (${gradientType})`, } ); } } catch (fileError) { // If file saving fails, we'll fall back to returning HTML content directly logger.warn('Failed to save HTML file, falling back to content', { error: fileError as Error, }); } return { success: true, data: { gradient_css: validatedParams.gradient_css, gradient_type: gradientType, color_count: gradientAnalysis.colorCount, preview_shapes: validatedParams.preview_shapes || ['rectangle'], size: validatedParams.size || [400, 300], has_interactive_controls: validatedParams.interactive_controls || false, file_path: result.filePath, file_name: result.fileName, file_size: result.fileSize, background_controls_enabled: result.backgroundControlsEnabled, accessibility_features: result.accessibilityFeatures, }, metadata: { execution_time: executionTime, tool: 'create_gradient_html', timestamp: new Date().toISOString(), color_space_used: 'sRGB', accessibility_notes: accessibilityNotes, recommendations: [ ...recommendations, 'HTML file saved with interactive background controls', 'Use Alt+T to toggle background theme', 'Use Alt+C to open color picker', ], }, visualizations: { html: result.htmlContent || `File saved: ${result.filePath}`, ...(visualizationResult?.html_file && { html_file: visualizationResult.html_file, }), }, export_formats: exportFormats, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const errorStack = error instanceof Error ? error.stack : undefined; return { success: false, error: { code: 'INTERNAL_ERROR', message: `Gradient HTML generation error: ${errorMessage}`, details: { errorMessage, errorStack, errorType: error?.constructor?.name || 'Unknown', }, suggestions: [ 'Verify the CSS gradient syntax is correct', 'Try with simpler gradient definitions', 'Check if all colors in the gradient are valid', ], }, metadata: { execution_time: Date.now() - startTime, tool: 'create_gradient_html', timestamp: new Date().toISOString(), }, }; } } function validateGradientCSS(gradientCSS: string): { isValid: boolean; error?: string; } { try { // Basic validation for CSS gradient syntax const trimmed = gradientCSS.trim(); // Check for basic gradient function patterns const gradientPatterns = [ /^linear-gradient\s*\(/i, /^radial-gradient\s*\(/i, /^conic-gradient\s*\(/i, /^repeating-linear-gradient\s*\(/i, /^repeating-radial-gradient\s*\(/i, ]; const hasValidPattern = gradientPatterns.some(pattern => pattern.test(trimmed) ); if (!hasValidPattern) { return { isValid: false, error: 'CSS must start with a valid gradient function (linear-gradient, radial-gradient, etc.)', }; } // Check for balanced parentheses const openParens = (trimmed.match(/\(/g) || []).length; const closeParens = (trimmed.match(/\)/g) || []).length; if (openParens !== closeParens) { return { isValid: false, error: 'Unbalanced parentheses in CSS gradient', }; } // Check for at least two colors (basic requirement for gradients) const colorMatches = trimmed.match(/#[0-9a-f]{3,6}|rgb\(|hsl\(|[a-z]+/gi); if (!colorMatches || colorMatches.length < 2) { return { isValid: false, error: 'Gradient must contain at least two colors', }; } return { isValid: true }; } catch (error) { return { isValid: false, error: `CSS validation error: ${error instanceof Error ? error.message : String(error)}`, }; } } function analyzeGradient(gradientCSS: string): { colorCount: number; hasLowContrast: boolean; colors: string[]; } { // Extract colors from gradient CSS const colorMatches = gradientCSS.match( /#[0-9a-f]{3,6}|rgb\([^)]+\)|hsl\([^)]+\)|rgba\([^)]+\)|hsla\([^)]+\)/gi ) || []; // Simple contrast analysis (basic implementation) const hasLowContrast = colorMatches.length > 1 && colorMatches.some((color, index) => { if (index === 0) return false; const prevColor = colorMatches[index - 1]; // This is a simplified check - in a real implementation, // you'd calculate actual contrast ratios return prevColor && color.toLowerCase() === prevColor.toLowerCase(); }); return { colorCount: colorMatches.length, hasLowContrast, colors: colorMatches, }; } function determineGradientType(gradientCSS: string): string { const trimmed = gradientCSS.trim().toLowerCase(); if (trimmed.startsWith('linear-gradient')) return 'linear'; if (trimmed.startsWith('radial-gradient')) return 'radial'; if (trimmed.startsWith('conic-gradient')) return 'conic'; if (trimmed.startsWith('repeating-linear-gradient')) return 'repeating-linear'; if (trimmed.startsWith('repeating-radial-gradient')) return 'repeating-radial'; return 'unknown'; } function generateGradientCSSExport( gradientCSS: string, gradientType: string ): string { return `.gradient-${gradientType} { background: ${gradientCSS}; } .gradient-fallback { background: ${gradientCSS}; /* Fallback for older browsers */ background: -webkit-${gradientCSS}; background: -moz-${gradientCSS}; background: -o-${gradientCSS}; }`; } function generateGradientSCSSExport( gradientCSS: string, gradientType: string ): string { return `$gradient-${gradientType}: ${gradientCSS}; @mixin gradient-${gradientType}() { background: $gradient-${gradientType}; background: -webkit-#{$gradient-${gradientType}}; background: -moz-#{$gradient-${gradientType}}; background: -o-#{$gradient-${gradientType}}; } .gradient-${gradientType} { @include gradient-${gradientType}(); }`; } export const createGradientHtmlTool: ToolHandler = { name: 'create_gradient_html', description: 'Generate HTML gradient preview visualizations with CSS code display and interactive controls', parameters: createGradientHtmlSchema.describe(), handler: async (params: unknown) => createGradientHtml(params as CreateGradientHtmlParams), };

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/keyurgolani/ColorMcp'

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