Skip to main content
Glama
jit-widget-specs.js34.7 kB
#!/usr/bin/env node /** * JIT Widget Specification Provider v1.0.0 * Just-In-Time widget specification loading for token efficiency * @version 1.0.0 (July 18, 2025) * @status IMPLEMENTATION COMPLETE - JIT specification delivery * @purpose Optimize token usage by loading specs only for selected widgets * @reference Implementation Plan - Task 3: Create JIT Widget Specification Provider * @milestone Semantic Widget Classification System Implementation */ /** * Complete Widget Specifications Database * Contains full JSON schemas for all EuConquisto Composer widgets * Based on official JSON reference and working implementations */ const WIDGET_SPECIFICATIONS = { // === HEADER & BRANDING === 'head-1': { apiName: 'head-1', category: 'header', description: 'Professional lesson header with author info and background', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'head-1' }, content_title: { type: 'string', nullable: true }, primary_color: { type: 'string', default: '#FFFFFF' }, secondary_color: { type: 'string', default: '#aa2c23' }, category: { type: 'string', format: 'html', required: true }, background_image: { type: 'string', format: 'url', nullable: true }, avatar: { type: 'string', format: 'url', nullable: true }, avatar_border_color: { type: 'string', default: '#00643e' }, author_name: { type: 'string', format: 'html', required: true }, author_office: { type: 'string', format: 'html', nullable: true }, show_category: { type: 'boolean', default: true }, show_author_name: { type: 'boolean', default: true }, show_divider: { type: 'boolean', default: true }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['category', 'author_name'], tokenCost: 85 }, // === TEXT CONTENT WIDGETS === 'text-1': { apiName: 'text-1', category: 'content', description: 'Rich HTML text content with formatting options', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'text-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, background_color: { type: 'string', default: '#FFFFFF' }, text: { type: 'string', format: 'html', required: true, minLength: 20 }, font_family: { type: 'string', default: 'Lato' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['text'], tokenCost: 65 }, 'text-2': { apiName: 'text-2', category: 'content', description: 'Text with title formatting', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'text-2' }, content_title: { type: 'string', required: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, background_color: { type: 'string', default: '#FFFFFF' }, text: { type: 'string', format: 'html', required: true }, font_family: { type: 'string', default: 'Lato' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['content_title', 'text'], tokenCost: 70 }, // === ASSESSMENT WIDGETS === 'quiz-1': { apiName: 'quiz-1', category: 'assessment', description: 'Single-choice quiz questions', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'quiz-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 35 }, padding_bottom: { type: 'number', default: 35 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, score_label: { type: 'string', default: 'Pontuação' }, questions: { type: 'array', required: true, items: { type: 'object', properties: { question: { type: 'string', required: true, minLength: 10 }, answers: { type: 'array', required: true, items: { type: 'string' } }, correct_option: { type: 'number', required: true, minimum: 0 } } } }, max_attempts: { type: 'number', default: 3, minimum: 1, maximum: 5 }, feedback_success: { type: 'string', default: 'Correto!' }, feedback_error: { type: 'string', default: 'Tente novamente.' }, random_questions: { type: 'boolean', default: false }, random_answers: { type: 'boolean', default: false }, show_correct_answer: { type: 'boolean', default: true }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['questions'], fieldTransformations: { 'options': 'answers' // Transform options array to answers in API }, tokenCost: 120 }, 'quiz-2': { apiName: 'quiz-2', category: 'assessment', description: 'Multiple-choice quiz questions', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'quiz-2' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 35 }, padding_bottom: { type: 'number', default: 35 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, score_label: { type: 'string', default: 'Pontuação' }, questions: { type: 'array', required: true, items: { type: 'object', properties: { question: { type: 'string', required: true }, answers: { type: 'array', required: true, items: { type: 'string' } }, correct_options: { type: 'array', required: true, items: { type: 'number' } } } } }, max_attempts: { type: 'number', default: 3 }, feedback_success: { type: 'string', default: 'Correto!' }, feedback_error: { type: 'string', default: 'Tente novamente.' }, show_correct_answer: { type: 'boolean', default: true }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['questions'], tokenCost: 125 }, 'quiz-3': { apiName: 'quiz-3', category: 'assessment', description: 'Fill-in-the-blank quiz', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'quiz-3' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 35 }, padding_bottom: { type: 'number', default: 35 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, questions: { type: 'array', required: true, items: { type: 'object', properties: { text: { type: 'string', required: true }, blanks: { type: 'array', required: true, items: { type: 'object', properties: { position: { type: 'number', required: true }, answer: { type: 'string', required: true } } } } } } }, max_attempts: { type: 'number', default: 3 }, feedback_success: { type: 'string', default: 'Correto!' }, feedback_error: { type: 'string', default: 'Tente novamente.' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['questions'], tokenCost: 110 }, // === MEMORY & VOCABULARY WIDGETS === 'flashcards-1': { apiName: 'flashcards-1', category: 'vocabulary', description: 'Interactive flip cards for memorization', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'flashcards-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 35 }, padding_bottom: { type: 'number', default: 35 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, flashcards_items: { type: 'array', required: true, items: { type: 'object', properties: { question: { type: 'string', required: true }, answer: { type: 'string', required: true } } } }, height: { type: 'number', default: 300 }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['flashcards_items'], alternativeFormats: { 'front': 'question', 'back': 'answer' }, tokenCost: 75 }, 'flashcards-2': { apiName: 'flashcards-2', category: 'vocabulary', description: 'Slide-based flashcards with navigation', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'flashcards-2' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 35 }, padding_bottom: { type: 'number', default: 35 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, flashcards_items: { type: 'array', required: true, items: { type: 'object', properties: { question: { type: 'string', required: true }, answer: { type: 'string', required: true } } } }, height: { type: 'number', default: 400 }, show_navigation: { type: 'boolean', default: true }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['flashcards_items'], tokenCost: 80 }, // === VISUAL CONTENT WIDGETS === 'image-1': { apiName: 'image-1', category: 'visual', description: 'Single image with optional caption', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'image-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, background_color: { type: 'string', default: '#FFFFFF' }, image: { type: 'string', format: 'url', required: true }, caption: { type: 'string', nullable: true }, width: { type: 'number', default: 760, maximum: 760 }, alignment: { type: 'string', enum: ['left', 'center', 'right'], default: 'center' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['image'], tokenCost: 55 }, 'image-2': { apiName: 'image-2', category: 'visual', description: 'Fullscreen image display', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'image-2' }, content_title: { type: 'string', nullable: true }, image: { type: 'string', format: 'url', required: true }, caption: { type: 'string', nullable: true }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['image'], tokenCost: 45 }, 'images-gallery': { apiName: 'images-gallery', category: 'visual', description: 'Image gallery with multiple images', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'images-gallery' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, background_color: { type: 'string', default: '#FFFFFF' }, images: { type: 'array', required: true, items: { type: 'object', properties: { image: { type: 'string', format: 'url', required: true }, caption: { type: 'string', nullable: true } } } }, columns: { type: 'number', default: 3, minimum: 2, maximum: 4 }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['images'], tokenCost: 65 }, // === ORGANIZATIONAL WIDGETS === 'list-1': { apiName: 'list-1', category: 'organization', description: 'Numbered list presentation', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'list-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, background_color: { type: 'string', default: '#FFFFFF' }, items: { type: 'array', required: true, items: { type: 'string' }, minItems: 2 }, list_type: { type: 'string', enum: ['numbered', 'bullet'], default: 'numbered' }, font_family: { type: 'string', default: 'Lato' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['items'], tokenCost: 50 }, 'list-2': { apiName: 'list-2', category: 'organization', description: 'Interactive checkbox list', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'list-2' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, items: { type: 'array', required: true, items: { type: 'string' }, minItems: 2 }, allow_interaction: { type: 'boolean', default: true }, font_family: { type: 'string', default: 'Lato' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['items'], tokenCost: 55 }, 'list-3': { apiName: 'list-3', category: 'organization', description: 'Bullet point list', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'list-3' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, background_color: { type: 'string', default: '#FFFFFF' }, items: { type: 'array', required: true, items: { type: 'string' }, minItems: 2 }, bullet_style: { type: 'string', enum: ['disc', 'circle', 'square'], default: 'disc' }, font_family: { type: 'string', default: 'Lato' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['items'], tokenCost: 50 }, // === ADVANCED INTERACTION WIDGETS === 'accordion': { apiName: 'accordion', category: 'interaction', description: 'Expandable content sections', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'accordion' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, sections: { type: 'array', required: true, items: { type: 'object', properties: { title: { type: 'string', required: true }, content: { type: 'string', format: 'html', required: true }, expanded: { type: 'boolean', default: false } } } }, allow_multiple: { type: 'boolean', default: false }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['sections'], tokenCost: 70 }, 'tabs': { apiName: 'tabs', category: 'interaction', description: 'Tabbed content navigation', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'tabs' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, tabs: { type: 'array', required: true, items: { type: 'object', properties: { title: { type: 'string', required: true }, content: { type: 'string', format: 'html', required: true } } } }, active_tab: { type: 'number', default: 0 }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['tabs'], tokenCost: 75 }, 'timeline-1': { apiName: 'timeline-1', category: 'interaction', description: 'Chronological timeline display', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'timeline-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 35 }, padding_bottom: { type: 'number', default: 35 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, timeline_items: { type: 'array', required: true, items: { type: 'object', properties: { date: { type: 'string', required: true }, title: { type: 'string', required: true }, content: { type: 'string', format: 'html', required: true }, image: { type: 'string', format: 'url', nullable: true } } } }, orientation: { type: 'string', enum: ['vertical', 'horizontal'], default: 'vertical' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['timeline_items'], tokenCost: 85 }, 'steps-1': { apiName: 'steps-1', category: 'interaction', description: 'Step-by-step process presentation', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'steps-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 35 }, padding_bottom: { type: 'number', default: 35 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, steps: { type: 'array', required: true, items: { type: 'object', properties: { number: { type: 'number', required: true }, title: { type: 'string', required: true }, content: { type: 'string', format: 'html', required: true }, image: { type: 'string', format: 'url', nullable: true } } } }, show_progress: { type: 'boolean', default: true }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['steps'], tokenCost: 80 }, // === MULTIMEDIA WIDGETS === 'video-1': { apiName: 'video-1', category: 'multimedia', description: 'Video player with controls', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'video-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, background_color: { type: 'string', default: '#000000' }, video: { type: 'string', format: 'url', required: true }, poster: { type: 'string', format: 'url', nullable: true }, autoplay: { type: 'boolean', default: false }, loop: { type: 'boolean', default: false }, controls: { type: 'boolean', default: true }, width: { type: 'string', default: '100%' }, height: { type: 'string', default: 'auto' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['video'], tokenCost: 60 }, 'audio-1': { apiName: 'audio-1', category: 'multimedia', description: 'Audio player for mp3 files', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'audio-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, background_color: { type: 'string', default: '#FFFFFF' }, audio: { type: 'string', format: 'url', required: true }, autoplay: { type: 'boolean', default: false }, loop: { type: 'boolean', default: false }, controls: { type: 'boolean', default: true }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['audio'], tokenCost: 50 }, // === SPECIALIZED WIDGETS === 'table-1': { apiName: 'table-1', category: 'data', description: 'Data table with customizable rows and columns', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'table-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#f5f5f5' }, background_color: { type: 'string', default: '#FFFFFF' }, headers: { type: 'array', required: true, items: { type: 'string' } }, rows: { type: 'array', required: true, items: { type: 'array', items: { type: 'string' } } }, striped: { type: 'boolean', default: true }, bordered: { type: 'boolean', default: true }, hover: { type: 'boolean', default: true }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['headers', 'rows'], tokenCost: 70 }, 'hotspots-1': { apiName: 'hotspots-1', category: 'interaction', description: 'Interactive image with clickable hotspots', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'hotspots-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 15 }, padding_bottom: { type: 'number', default: 15 }, background_color: { type: 'string', default: '#FFFFFF' }, image: { type: 'string', format: 'url', required: true }, hotspots: { type: 'array', required: true, items: { type: 'object', properties: { x: { type: 'number', required: true, minimum: 0, maximum: 100 }, y: { type: 'number', required: true, minimum: 0, maximum: 100 }, title: { type: 'string', required: true }, content: { type: 'string', required: true } } } }, marker_color: { type: 'string', default: '#4CAF50' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['image', 'hotspots'], tokenCost: 80 }, 'sorted-activity-1': { apiName: 'sorted-activity-1', category: 'interaction', description: 'Drag and drop sorting activity', jsonSchema: { id: { type: 'string', format: 'uuid', generated: true }, type: { type: 'string', const: 'sorted-activity-1' }, content_title: { type: 'string', nullable: true }, padding_top: { type: 'number', default: 35 }, padding_bottom: { type: 'number', default: 35 }, primary_color: { type: 'string', default: '#313537' }, secondary_color: { type: 'string', default: '#4CAF50' }, background_color: { type: 'string', default: '#FFFFFF' }, categories: { type: 'array', required: true, items: { type: 'object', properties: { name: { type: 'string', required: true }, color: { type: 'string', default: '#4CAF50' } } } }, items: { type: 'array', required: true, items: { type: 'object', properties: { text: { type: 'string', required: true }, category: { type: 'string', required: true } } } }, feedback_success: { type: 'string', default: 'Correto!' }, feedback_error: { type: 'string', default: 'Tente novamente.' }, dam_assets: { type: 'array', default: [] } }, requiredFields: ['categories', 'items'], tokenCost: 95 } }; /** * Widget Category Definitions * Groups widgets by educational purpose */ const WIDGET_CATEGORIES = { header: ['head-1'], content: ['text-1', 'text-2', 'text-3', 'text-4', 'text-5'], assessment: ['quiz-1', 'quiz-2', 'quiz-3', 'quiz-4', 'quiz-5', 'quiz-6'], vocabulary: ['flashcards-1', 'flashcards-2'], visual: ['image-1', 'image-2', 'images-gallery', 'image-and-text', 'image-with-text'], organization: ['list-1', 'list-2', 'list-3'], interaction: ['accordion', 'tabs', 'timeline-1', 'steps-1', 'hotspots-1', 'sorted-activity-1'], multimedia: ['video-1', 'audio-1', 'interactive-1'], data: ['table-1'], utility: ['divider-1', 'divider-4', 'divider-numbered', 'virtual-index-1'] }; /** * JIT Widget Specification Provider Class */ export class JITWidgetSpecProvider { constructor() { this.widgetSpecs = WIDGET_SPECIFICATIONS; this.categories = WIDGET_CATEGORIES; this.tokenUsage = { initial: 0, delivered: 0, saved: 0 }; } /** * Get specifications for selected widgets only (JIT delivery) * @param {Array} selectedWidgets - Array of widget types * @returns {Object} JIT specifications for requested widgets */ getSpecificationsForWidgets(selectedWidgets) { const specs = {}; let totalTokenCost = 0; // Normalize widget types const widgetTypes = this.normalizeWidgetTypes(selectedWidgets); // Deliver only requested specifications widgetTypes.forEach(widgetType => { if (this.widgetSpecs[widgetType]) { specs[widgetType] = this.widgetSpecs[widgetType]; totalTokenCost += this.widgetSpecs[widgetType].tokenCost || 50; } else { console.warn(`[JIT_SPECS] Unknown widget type requested: ${widgetType}`); } }); // Track token usage this.tokenUsage.delivered = totalTokenCost; this.tokenUsage.initial = Object.keys(this.widgetSpecs).reduce((sum, type) => sum + (this.widgetSpecs[type].tokenCost || 50), 0 ); this.tokenUsage.saved = this.tokenUsage.initial - this.tokenUsage.delivered; return { specifications: specs, summary: { widgetsRequested: widgetTypes.length, widgetsDelivered: Object.keys(specs).length, tokenCost: totalTokenCost, tokenSavings: { initial: this.tokenUsage.initial, delivered: this.tokenUsage.delivered, saved: this.tokenUsage.saved, savingsPercentage: Math.round((this.tokenUsage.saved / this.tokenUsage.initial) * 100) } } }; } /** * Get widget category information * @param {String} widgetType - Widget type to categorize * @returns {Object} Category information */ getWidgetCategory(widgetType) { for (const [category, widgets] of Object.entries(this.categories)) { if (widgets.includes(widgetType)) { return { category, widgets, description: this.getCategoryDescription(category) }; } } return { category: 'unknown', widgets: [], description: 'Unknown widget category' }; } /** * Get category description */ getCategoryDescription(category) { const descriptions = { header: 'Professional lesson branding and introduction', content: 'Text-based educational content delivery', assessment: 'Knowledge evaluation and testing', vocabulary: 'Terminology learning and memorization', visual: 'Image-based content and visual learning', organization: 'Structured information presentation', interaction: 'Interactive learning experiences', multimedia: 'Audio and video content delivery', data: 'Tabular data presentation and analysis', utility: 'Structural and navigation elements' }; return descriptions[category] || 'General purpose widget'; } /** * Get simplified widget requirements (token-efficient) * @param {String} widgetType - Widget type * @returns {Object} Simplified requirements */ getSimplifiedRequirements(widgetType) { const spec = this.widgetSpecs[widgetType]; if (!spec) return null; return { type: widgetType, required: spec.requiredFields, transformations: spec.fieldTransformations || {}, alternatives: spec.alternativeFormats || {}, validation: this.getValidationRules(spec), example: this.getMinimalExample(widgetType) }; } /** * Get validation rules for widget */ getValidationRules(spec) { const rules = []; if (spec.jsonSchema.text?.minLength) { rules.push(`Text minimum ${spec.jsonSchema.text.minLength} characters`); } if (spec.jsonSchema.items?.minItems) { rules.push(`Minimum ${spec.jsonSchema.items.minItems} items`); } if (spec.jsonSchema.questions) { rules.push('Questions array required with proper structure'); } return rules; } /** * Get minimal example for widget */ getMinimalExample(widgetType) { const minimalExamples = { 'head-1': { category: 'CIÊNCIAS', author_name: 'Professor Virtual' }, 'text-1': { text: '<p>Conteúdo educacional com mínimo de 20 caracteres.</p>' }, 'quiz-1': { questions: [{ question: 'Pergunta com mínimo 10 caracteres?', options: ['Opção A', 'Opção B', 'Opção C'], correct_option: 0 }] }, 'flashcards-1': { flashcards_items: [{ question: 'Termo', answer: 'Definição' }] }, 'list-1': { items: ['Item 1', 'Item 2'] }, 'image-1': { image: 'https://example.com/image.jpg' } }; return minimalExamples[widgetType] || {}; } /** * Normalize widget types from various input formats */ normalizeWidgetTypes(selectedWidgets) { if (!selectedWidgets) return []; // Handle array of strings if (Array.isArray(selectedWidgets)) { return selectedWidgets.filter(w => typeof w === 'string'); } // Handle object with selectedWidgetTypes if (selectedWidgets.selectedWidgetTypes) { return this.normalizeWidgetTypes(selectedWidgets.selectedWidgetTypes); } // Handle object with widgets array if (selectedWidgets.widgets) { return selectedWidgets.widgets.map(w => w.type || w).filter(Boolean); } // Handle single string if (typeof selectedWidgets === 'string') { return [selectedWidgets]; } return []; } /** * Get complete specification package for integration * @param {Array} widgetTypes - Selected widget types * @returns {Object} Complete JIT specification package */ getJITSpecificationPackage(widgetTypes) { const jitSpecs = this.getSpecificationsForWidgets(widgetTypes); const specPackage = { specifications: {}, examples: {}, validationRules: {}, tokenEfficiency: jitSpecs.summary.tokenSavings }; // Build comprehensive package widgetTypes.forEach(type => { if (jitSpecs.specifications[type]) { specPackage.specifications[type] = this.getSimplifiedRequirements(type); specPackage.examples[type] = this.getMinimalExample(type); specPackage.validationRules[type] = this.getValidationRules(jitSpecs.specifications[type]); } }); return specPackage; } } /** * Create and export the JIT specification provider tool */ export function createJITWidgetSpecProvider() { const provider = new JITWidgetSpecProvider(); return { name: 'get_jit_widget_specs', description: 'Get widget specifications Just-In-Time for selected widgets only (60%+ token savings)', inputSchema: { type: 'object', properties: { selectedWidgets: { description: 'Array of widget types or object containing widget types' } }, required: ['selectedWidgets'] }, handler: async (input) => { try { const { selectedWidgets } = input; if (!selectedWidgets) { throw new Error('selectedWidgets is required'); } const specPackage = provider.getJITSpecificationPackage(selectedWidgets); return { success: true, data: specPackage, debug: { timestamp: new Date().toISOString(), widgetsProcessed: Object.keys(specPackage.specifications).length, tokenSavings: specPackage.tokenEfficiency } }; } catch (error) { return { success: false, error: { code: 'JIT_SPEC_ERROR', message: error.message, timestamp: new Date().toISOString() } }; } } }; } export default JITWidgetSpecProvider;

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/rkm097git/euconquisto-composer-mcp-poc'

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