#!/usr/bin/env node /** * MCP Animation Frame Generator * Usage: node generate.js configs/your-config.json */ import fs from 'fs'; import path from 'path'; const configPath = process.argv[2]; if (!configPath) { console.error('Usage: node generate.js configs/your-config.json'); process.exit(1); } const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const outputDir = path.join('output', config.name); fs.mkdirSync(outputDir, { recursive: true }); // Generate panel HTML based on type function generatePanel(panel, index) { const gridSpan = panel.gridSpan === 2 ? 'grid-row: span 2;' : ''; let content = ''; switch (panel.type) { case 'list': content = generateListPanel(panel); break; case 'stats': content = generateStatsPanel(panel); break; case 'chart': content = generateChartPanel(panel); break; case 'chat': content = generateChatPanel(panel); break; default: content = '
Unknown panel type
'; } return `
${panel.icon}
${panel.title}
${content}
`; } function generateListPanel(panel) { const items = panel.items.map(item => `
${item.avatar}
${item.name}
${item.status}
${item.meta}
`).join(''); return `
${items}
`; } function generateStatsPanel(panel) { const stats = panel.stats.map(stat => `
${stat.label} ${stat.value} ${stat.change ? `${stat.change}` : ''}
`).join(''); const highlight = panel.highlight ? `
${panel.highlight.label}
${panel.highlight.value}
` : ''; return `
${stats}
${highlight}`; } function generateChartPanel(panel) { return `
${panel.centerLabel}
${panel.bottomValue}
${panel.bottomLabel}
`; } function generateChatPanel(panel) { const messages = panel.messages.map(msg => `
${msg.text}
`).join(''); return `
${messages}
`; } // Base HTML template function generateHTML(config, frameType) { const showUserMessage = ['first-exchange', 'typing-second', 'loading', 'final'].includes(frameType); const showAiResponse = ['first-exchange', 'typing-second', 'loading', 'final'].includes(frameType); const showPanels = ['loading', 'final'].includes(frameType); const showLoading = frameType === 'loading'; const showTyping = frameType === 'typing' || frameType === 'typing-second'; const panels = showPanels ? config.panels.map((p, i) => generatePanel(p, i)).join('') : ''; return ` ${config.title}
${config.title}
${showUserMessage ? `
${config.userPrompt}
` : ''} ${showTyping && !showAiResponse ? `
` : ''} ${showAiResponse ? `
${config.aiResponse}
${showPanels ? `
${panels}
` : ''}
` : ''}
Type a message...
`; } // Generate all frames const frames = [ { name: 'frame-01-empty', type: 'empty' }, { name: 'frame-02-typing', type: 'typing' }, { name: 'frame-03-first-exchange', type: 'first-exchange' }, { name: 'frame-04-typing-second', type: 'typing-second' }, { name: 'frame-05-loading', type: 'loading' }, { name: 'frame-06-final', type: 'final' } ]; frames.forEach(frame => { const html = generateHTML(config, frame.type); const filePath = path.join(outputDir, `${frame.name}.html`); fs.writeFileSync(filePath, html); console.log(`Generated: ${filePath}`); }); console.log(`\n✓ Generated ${frames.length} frames in ${outputDir}/`); console.log(`\nTo preview: open ${outputDir}/frame-06-final.html`); console.log(`To export: use browser screenshot or puppeteer`);