UNPKG

hatch-slidev-builder-mcp

Version:

A comprehensive MCP server for creating Slidev presentations with component library, interactive elements, and team collaboration features

232 lines (231 loc) 9.38 kB
/** * Layer 3: Asset Intelligence Engine * Smart asset curation and management with semantic matching */ export class AssetIntelligenceEngine { /** * Curate assets based on content context and brand guidelines */ static async curateAssets(query) { const searchTerms = this.extractSearchTerms(query.content_context); const styleFilter = this.getStyleFilter(query.preferred_style || 'professional'); // Search across multiple sources const unsplashAssets = await this.searchUnsplash(searchTerms, styleFilter); const iconifyAssets = await this.searchIconify(searchTerms); const freepikAssets = await this.searchFreepik(searchTerms, styleFilter); // Combine and score assets const allAssets = [...unsplashAssets, ...iconifyAssets, ...freepikAssets]; const scoredAssets = this.scoreAssets(allAssets, query); // Filter by brand compliance and cultural appropriateness const compliantAssets = scoredAssets.filter(asset => asset.brand_compliance && asset.cultural_appropriateness); // Sort by semantic score and select top candidates const recommendedAssets = compliantAssets .sort((a, b) => b.semantic_score - a.semantic_score) .slice(0, 5); const fallbackAssets = scoredAssets .filter(asset => !recommendedAssets.includes(asset)) .slice(0, 3); return { assets: recommendedAssets, reasoning: this.generateReasoning(recommendedAssets, query), confidence_score: this.calculateConfidenceScore(recommendedAssets), fallback_options: fallbackAssets }; } /** * Extract search terms from content context */ static extractSearchTerms(content) { // Remove common words and extract meaningful terms const commonWords = ['the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by']; const words = content.toLowerCase() .replace(/[^\w\s]/g, '') .split(/\s+/) .filter(word => word.length > 3 && !commonWords.includes(word)); // Extract key business and technical terms const businessTerms = words.filter(word => ['business', 'strategy', 'growth', 'innovation', 'technology', 'solution', 'service'].includes(word)); // Combine unique terms const uniqueTerms = [...new Set([...businessTerms, ...words.slice(0, 5)])]; return uniqueTerms.slice(0, 10); // Limit to top 10 terms } /** * Get style filter based on preferences */ static getStyleFilter(style) { const filters = { professional: { color: 'blue', orientation: 'landscape', keywords: ['business', 'corporate', 'professional', 'clean', 'minimal'] }, casual: { color: null, orientation: null, keywords: ['friendly', 'approachable', 'modern', 'colorful'] }, technical: { color: 'black_and_white', orientation: 'landscape', keywords: ['technical', 'engineering', 'data', 'diagram', 'schematic'] }, creative: { color: null, orientation: null, keywords: ['creative', 'artistic', 'innovative', 'unique', 'bold'] } }; return filters[style] || filters.professional; } /** * Search Unsplash for relevant images */ static async searchUnsplash(terms, styleFilter) { const searchQuery = terms.join(' '); try { // This would integrate with the Unsplash MCP // For now, return mock data structure return [ { id: 'unsplash_1', type: 'image', source: 'unsplash', url: 'https://images.unsplash.com/example', alt_text: `Professional ${searchQuery} image`, tags: terms, semantic_score: 0.8, brand_compliance: true, cultural_appropriateness: true, license: 'free', dimensions: { width: 1920, height: 1080 } } ]; } catch (error) { console.error('Unsplash search failed:', error); return []; } } /** * Search Iconify for relevant icons */ static async searchIconify(terms) { try { // This would integrate with the Iconify MCP return terms.slice(0, 3).map((term, index) => ({ id: `iconify_${index}`, type: 'icon', source: 'iconify', url: `https://api.iconify.design/mdi/${term}.svg`, alt_text: `${term} icon`, tags: [term], semantic_score: 0.9, brand_compliance: true, cultural_appropriateness: true, license: 'free', dimensions: { width: 24, height: 24 } })); } catch (error) { console.error('Iconify search failed:', error); return []; } } /** * Search Freepik for premium assets */ static async searchFreepik(terms, styleFilter) { try { // This would integrate with the Freepik MCP return [ { id: 'freepik_1', type: 'image', source: 'freepik', url: 'https://freepik.com/example', alt_text: `Premium ${terms[0]} illustration`, tags: terms, semantic_score: 0.85, brand_compliance: true, cultural_appropriateness: true, license: 'premium', dimensions: { width: 1920, height: 1080 } } ]; } catch (error) { console.error('Freepik search failed:', error); return []; } } /** * Score assets based on relevance and context */ static scoreAssets(assets, query) { return assets.map(asset => { let score = asset.semantic_score; // Boost score for matching slide type if (query.slide_type === 'hero' && asset.type === 'image') { score += 0.1; } else if (query.slide_type === 'process' && asset.type === 'icon') { score += 0.15; } // Boost for professional style with executive audience if (query.target_audience === 'executive' && asset.tags.includes('professional')) { score += 0.1; } // Penalty for premium assets unless specifically requested if (asset.license === 'premium' && !query.content_context.includes('premium')) { score -= 0.05; } return { ...asset, semantic_score: Math.min(1.0, score) }; }); } /** * Generate reasoning for asset recommendations */ static generateReasoning(assets, query) { const assetTypes = [...new Set(assets.map(a => a.type))]; const sources = [...new Set(assets.map(a => a.source))]; return `Selected ${assets.length} assets (${assetTypes.join(', ')}) from ${sources.join(', ')} ` + `optimized for ${query.slide_type} slide targeting ${query.target_audience} audience. ` + `Assets chosen for high semantic relevance and brand compliance.`; } /** * Calculate confidence score for recommendations */ static calculateConfidenceScore(assets) { if (assets.length === 0) return 0; const avgSemanticScore = assets.reduce((sum, asset) => sum + asset.semantic_score, 0) / assets.length; const brandComplianceRate = assets.filter(a => a.brand_compliance).length / assets.length; return (avgSemanticScore * 0.7) + (brandComplianceRate * 0.3); } /** * Validate asset against brand guidelines */ static validateBrandCompliance(asset, brandGuidelines) { // Implementation would check against actual brand guidelines // For now, basic validation if (brandGuidelines?.colors && asset.tags.some(tag => brandGuidelines.colors.includes(tag.toLowerCase()))) { return true; } if (brandGuidelines?.style && asset.tags.includes(brandGuidelines.style)) { return true; } // Default to true for professional content return asset.tags.includes('professional') || asset.tags.includes('business'); } /** * Check cultural appropriateness */ static checkCulturalAppropriateness(asset, context) { // Basic implementation - would be enhanced with actual cultural sensitivity analysis const sensitiveTerms = ['religious', 'political', 'controversial']; const assetContent = asset.alt_text.toLowerCase() + ' ' + asset.tags.join(' ').toLowerCase(); return !sensitiveTerms.some(term => assetContent.includes(term)); } }