jit-widget-specs.js•34.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;