UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

1,531 lines (1,435 loc) 53 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.VideoTutorialGenerator = void 0; const events_1 = require("events"); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); class VideoTutorialGenerator extends events_1.EventEmitter { constructor(config = {}) { super(); this.tutorials = new Map(); this.guides = new Map(); this.recordings = new Map(); this.config = { outputPath: './video-tutorials', recordingsPath: './recordings', transcriptsPath: './transcripts', videoFormats: ['mp4', 'webm'], resolutions: ['1080p'], generateTranscripts: true, generateSubtitles: true, generateChapters: true, includeCodeSnippets: true, includeInteractiveElements: true, publishTargets: ['website', 'docs'], ...config }; this.initializeBuiltInTutorials(); this.initializeBuiltInGuides(); } async generateVideoTutorials() { this.emit('generation:start'); try { // Create output directories await fs.ensureDir(this.config.outputPath); await fs.ensureDir(this.config.recordingsPath); await fs.ensureDir(this.config.transcriptsPath); // Generate video tutorials let videosCreated = 0; for (const [id, tutorial] of this.tutorials) { this.emit('tutorial:start', id); // Generate script await this.generateScript(tutorial); // Create visual assets await this.createVisualAssets(tutorial); // Generate video (simulated) await this.generateVideo(tutorial); // Generate transcripts if (this.config.generateTranscripts) { await this.generateTranscript(tutorial); } // Generate subtitles if (this.config.generateSubtitles) { await this.generateSubtitles(tutorial); } videosCreated++; this.emit('tutorial:complete', id); } // Generate interactive guides let guidesCreated = 0; for (const [id, guide] of this.guides) { this.emit('guide:start', id); await this.generateInteractiveGuide(guide); guidesCreated++; this.emit('guide:complete', id); } // Create tutorial index await this.generateTutorialIndex(); // Create interactive learning platform if (this.config.includeInteractiveElements) { await this.generateLearningPlatform(); } // Publish to targets const publishedTo = []; for (const target of this.config.publishTargets || []) { await this.publishToTarget(target); publishedTo.push(target); } const result = { tutorialsGenerated: this.tutorials.size, videosCreated, guidesCreated, totalDuration: this.calculateTotalDuration(), outputPath: this.config.outputPath, publishedTo }; this.emit('generation:complete', result); return result; } catch (error) { this.emit('generation:error', error); throw error; } } initializeBuiltInTutorials() { // Getting Started Tutorial this.addTutorial({ id: 'getting-started', title: 'Getting Started with Re-Shell', description: 'Learn how to install and create your first Re-Shell project', duration: 600, // 10 minutes difficulty: 'beginner', topics: ['installation', 'project-setup', 'basic-commands'], script: { scenes: [ { id: 'intro', title: 'Welcome to Re-Shell', duration: 30, type: 'intro', content: { narration: 'Welcome to Re-Shell, the modern microfrontend framework that makes building scalable applications easy.', actions: [ { timestamp: 0, type: 'navigate', value: 'https://re-shell.dev', description: 'Show Re-Shell homepage' } ] }, transitions: [{ type: 'fade', duration: 1 }] }, { id: 'installation', title: 'Installing Re-Shell', duration: 120, type: 'demo', content: { narration: 'Let\'s start by installing Re-Shell globally using npm.', actions: [ { timestamp: 5, type: 'type', value: 'npm install -g @re-shell/cli', description: 'Install Re-Shell CLI' } ], codeBlocks: [ { timestamp: 10, language: 'bash', code: 'npm install -g @re-shell/cli', duration: 5 } ] }, transitions: [{ type: 'slide', duration: 1, direction: 'left' }] } ], captions: [ { timestamp: 0, duration: 5, text: 'Welcome to Re-Shell' }, { timestamp: 35, duration: 10, text: 'Install Re-Shell CLI globally' } ], annotations: [] }, assets: { thumbnails: [], overlays: [], images: [], fonts: [] }, chapters: [ { id: 'ch1', title: 'Introduction', startTime: 0, endTime: 30, keyPoints: ['What is Re-Shell', 'Why use microfrontends'] }, { id: 'ch2', title: 'Installation', startTime: 30, endTime: 150, keyPoints: ['Installing CLI', 'Verifying installation'] } ], metadata: { tags: ['tutorial', 'beginner', 'installation'], category: 'Getting Started', language: 'en', targetAudience: ['developers', 'beginners'], learningObjectives: [ 'Install Re-Shell CLI', 'Create first project', 'Understand basic commands' ] } }); // Advanced Workspace Tutorial this.addTutorial({ id: 'advanced-workspace', title: 'Advanced Workspace Management', description: 'Master workspace configuration and optimization techniques', duration: 1200, // 20 minutes difficulty: 'advanced', topics: ['workspace', 'configuration', 'optimization', 'scaling'], prerequisites: ['getting-started'], script: { scenes: [ { id: 'workspace-intro', title: 'Understanding Workspaces', duration: 60, type: 'explanation', content: { narration: 'Re-Shell workspaces allow you to manage multiple interconnected applications efficiently.', actions: [], diagrams: [ { timestamp: 10, type: 'architecture', content: 'graph LR\n A[Workspace] --> B[App 1]\n A --> C[App 2]\n A --> D[Shared Libs]', format: 'mermaid', duration: 20 } ] }, transitions: [{ type: 'zoom', duration: 1 }] } ], captions: [], annotations: [] }, assets: { thumbnails: [], overlays: [], images: [], fonts: [] }, chapters: [ { id: 'ch1', title: 'Workspace Concepts', startTime: 0, endTime: 300, keyPoints: ['Workspace structure', 'Dependency management'] } ], metadata: { tags: ['advanced', 'workspace', 'optimization'], category: 'Advanced Topics', language: 'en', targetAudience: ['experienced-developers'], learningObjectives: [ 'Configure complex workspaces', 'Optimize build performance', 'Implement caching strategies' ] } }); // Debugging Tutorial this.addTutorial({ id: 'debugging-guide', title: 'Debugging Re-Shell Applications', description: 'Learn debugging techniques and tools for Re-Shell projects', duration: 900, // 15 minutes difficulty: 'intermediate', topics: ['debugging', 'troubleshooting', 'dev-tools'], script: { scenes: [ { id: 'debug-intro', title: 'Debugging Tools Overview', duration: 60, type: 'explanation', content: { narration: 'Re-Shell provides powerful debugging tools to help you identify and fix issues quickly.', actions: [ { timestamp: 10, type: 'highlight', target: '.debug-panel', description: 'Highlight debug panel' } ] }, transitions: [{ type: 'fade', duration: 1 }] } ], captions: [], annotations: [] }, assets: { thumbnails: [], overlays: [], images: [], fonts: [] }, chapters: [ { id: 'ch1', title: 'Debug Tools', startTime: 0, endTime: 300, keyPoints: ['Browser DevTools', 'Re-Shell Debug Mode'] } ], metadata: { tags: ['debugging', 'tools', 'troubleshooting'], category: 'Development', language: 'en', targetAudience: ['developers'], learningObjectives: [ 'Use debugging tools effectively', 'Identify common issues', 'Apply debugging strategies' ] } }); } initializeBuiltInGuides() { // Interactive Getting Started Guide this.addGuide({ id: 'interactive-getting-started', title: 'Interactive Getting Started Guide', description: 'Learn Re-Shell step by step with hands-on exercises', difficulty: 'beginner', estimatedTime: 30, steps: [ { id: 'step1', title: 'Install Re-Shell CLI', description: 'Install the Re-Shell command-line interface globally', type: 'action', content: { text: 'Run the following command to install Re-Shell CLI:', command: 'npm install -g @re-shell/cli', expectedOutput: 'added 1 package in' }, validation: { type: 'contains', value: 'added', successMessage: 'Great! Re-Shell CLI is now installed.' }, hints: ['Make sure you have Node.js 16+ installed'], nextStep: 'step2' }, { id: 'step2', title: 'Create Your First Project', description: 'Use Re-Shell CLI to create a new project', type: 'action', content: { text: 'Create a new Re-Shell project:', command: 're-shell init my-first-app', interactive: { type: 'terminal', allowedActions: ['type', 'enter'] } }, validation: { type: 'contains', value: 'Project created successfully', successMessage: 'Excellent! Your project is ready.' }, nextStep: 'step3' }, { id: 'step3', title: 'Explore Project Structure', description: 'Understand the generated project structure', type: 'instruction', content: { text: 'Your project structure should look like this:', code: `my-first-app/ ├── packages/ │ └── shell/ │ ├── src/ │ └── package.json ├── re-shell.config.json └── package.json`, diagram: 'Project structure diagram' }, nextStep: 'step4' }, { id: 'step4', title: 'Start Development Server', description: 'Run your application in development mode', type: 'action', content: { text: 'Start the development server:', command: 'npm run dev', expectedOutput: 'Server running at http://localhost:3000' }, validation: { type: 'contains', value: 'Server running', successMessage: 'Perfect! Your app is now running.' } } ], checkpoints: [ { id: 'cp1', afterStep: 'step2', title: 'Project Creation Checkpoint', description: 'Verify project was created correctly', validation: { checks: [ { type: 'file_exists', target: 'my-first-app/package.json', expected: 'true', description: 'Project package.json exists' } ], requireAll: true } } ], achievements: [ { id: 'first-project', title: 'First Project', description: 'Created your first Re-Shell project', icon: '🎉', condition: { type: 'complete_guide', value: 1 }, points: 100 }, { id: 'speed-learner', title: 'Speed Learner', description: 'Completed guide in under 20 minutes', icon: '⚡', condition: { type: 'speed_run', value: 1200 }, points: 50 } ] }); // Advanced Configuration Guide this.addGuide({ id: 'advanced-config', title: 'Advanced Configuration Guide', description: 'Master Re-Shell configuration with interactive examples', difficulty: 'advanced', estimatedTime: 45, steps: [ { id: 'config1', title: 'Understanding Configuration', description: 'Learn about Re-Shell configuration system', type: 'instruction', content: { text: 'Re-Shell uses a layered configuration system', code: `// re-shell.config.js export default { workspace: { name: 'my-workspace', apps: ['./packages/*'] } }`, interactive: { type: 'editor', initialState: { file: 're-shell.config.js' } } }, nextStep: 'config2' } ], checkpoints: [], achievements: [] }); } addTutorial(tutorial) { this.tutorials.set(tutorial.id, tutorial); } addGuide(guide) { this.guides.set(guide.id, guide); } async generateScript(tutorial) { const scriptPath = path.join(this.config.transcriptsPath, `${tutorial.id}-script.json`); await fs.writeJson(scriptPath, tutorial.script, { spaces: 2 }); // Generate human-readable script const readableScript = this.generateReadableScript(tutorial); const readablePath = path.join(this.config.transcriptsPath, `${tutorial.id}-script.md`); await fs.writeFile(readablePath, readableScript); } generateReadableScript(tutorial) { let script = `# ${tutorial.title}\n\n`; script += `**Duration**: ${Math.floor(tutorial.duration / 60)} minutes\n`; script += `**Difficulty**: ${tutorial.difficulty}\n\n`; script += `## Script\n\n`; for (const scene of tutorial.script.scenes) { script += `### Scene: ${scene.title} (${scene.duration}s)\n\n`; if (scene.content.narration) { script += `**Narration**: ${scene.content.narration}\n\n`; } if (scene.content.actions.length > 0) { script += `**Actions**:\n`; for (const action of scene.content.actions) { script += `- [${action.timestamp}s] ${action.description}\n`; } script += '\n'; } if (scene.content.codeBlocks && scene.content.codeBlocks.length > 0) { script += `**Code Examples**:\n`; for (const code of scene.content.codeBlocks) { script += `\`\`\`${code.language}\n${code.code}\n\`\`\`\n\n`; } } } return script; } async createVisualAssets(tutorial) { const assetsPath = path.join(this.config.outputPath, tutorial.id, 'assets'); await fs.ensureDir(assetsPath); // Generate title card await this.generateTitleCard(tutorial, assetsPath); // Generate chapter cards for (const chapter of tutorial.chapters) { await this.generateChapterCard(chapter, assetsPath); } // Generate end screen await this.generateEndScreen(tutorial, assetsPath); } async generateTitleCard(tutorial, outputPath) { const titleCard = ` <svg width="1920" height="1080" xmlns="http://www.w3.org/2000/svg"> <rect width="1920" height="1080" fill="#1a1a1a"/> <text x="960" y="480" text-anchor="middle" font-family="Arial" font-size="72" fill="white"> ${tutorial.title} </text> <text x="960" y="560" text-anchor="middle" font-family="Arial" font-size="36" fill="#666"> ${tutorial.description} </text> <text x="960" y="680" text-anchor="middle" font-family="Arial" font-size="24" fill="#888"> Duration: ${Math.floor(tutorial.duration / 60)} minutes | Difficulty: ${tutorial.difficulty} </text> </svg>`; await fs.writeFile(path.join(outputPath, 'title-card.svg'), titleCard); } async generateChapterCard(chapter, outputPath) { const chapterCard = ` <svg width="1920" height="1080" xmlns="http://www.w3.org/2000/svg"> <rect width="1920" height="1080" fill="#1a1a1a"/> <text x="960" y="480" text-anchor="middle" font-family="Arial" font-size="64" fill="white"> ${chapter.title} </text> <g transform="translate(960, 580)"> ${chapter.keyPoints.map((point, i) => `<text x="0" y="${i * 40}" text-anchor="middle" font-family="Arial" font-size="28" fill="#888">• ${point}</text>`).join('\n')} </g> </svg>`; await fs.writeFile(path.join(outputPath, `chapter-${chapter.id}.svg`), chapterCard); } async generateEndScreen(tutorial, outputPath) { const endScreen = ` <svg width="1920" height="1080" xmlns="http://www.w3.org/2000/svg"> <rect width="1920" height="1080" fill="#1a1a1a"/> <text x="960" y="400" text-anchor="middle" font-family="Arial" font-size="72" fill="white"> Thanks for watching! </text> <text x="960" y="500" text-anchor="middle" font-family="Arial" font-size="36" fill="#666"> Check out more tutorials at re-shell.dev/tutorials </text> <text x="960" y="700" text-anchor="middle" font-family="Arial" font-size="28" fill="#888"> Next: ${tutorial.metadata.relatedTutorials?.[0] || 'Advanced Topics'} </text> </svg>`; await fs.writeFile(path.join(outputPath, 'end-screen.svg'), endScreen); } async generateVideo(tutorial) { // In a real implementation, this would use ffmpeg or similar // For now, we'll create a placeholder video file const videoPath = path.join(this.config.outputPath, tutorial.id); await fs.ensureDir(videoPath); for (const format of this.config.videoFormats || []) { const filePath = path.join(videoPath, `${tutorial.id}.${format}`); await fs.writeFile(filePath, `Video placeholder for ${tutorial.title}`); // Create recording entry const recording = { id: `${tutorial.id}-${format}`, sceneId: 'all', filePath, format, resolution: this.config.resolutions[0], duration: tutorial.duration, fileSize: 0, metadata: { recordedAt: new Date(), device: 'Generated', software: 'Re-Shell Video Generator', framerate: 30, bitrate: 5000, audioChannels: 2 } }; this.recordings.set(recording.id, recording); } } async generateTranscript(tutorial) { const transcriptPath = path.join(this.config.transcriptsPath, `${tutorial.id}-transcript.txt`); let transcript = `${tutorial.title}\n`; transcript += `${'='.repeat(tutorial.title.length)}\n\n`; for (const scene of tutorial.script.scenes) { transcript += `[${this.formatTime(this.getSceneStartTime(scene, tutorial))}] ${scene.title}\n\n`; if (scene.content.narration) { transcript += `${scene.content.narration}\n\n`; } for (const caption of tutorial.script.captions) { if (this.isInScene(caption.timestamp, scene, tutorial)) { transcript += `[${this.formatTime(caption.timestamp)}] ${caption.text}\n`; } } transcript += '\n'; } await fs.writeFile(transcriptPath, transcript); } async generateSubtitles(tutorial) { // Generate WebVTT format const vttPath = path.join(this.config.outputPath, tutorial.id, `${tutorial.id}.vtt`); let vtt = 'WEBVTT\n\n'; for (const caption of tutorial.script.captions) { const start = this.formatTimeVTT(caption.timestamp); const end = this.formatTimeVTT(caption.timestamp + caption.duration); vtt += `${start} --> ${end}\n${caption.text}\n\n`; } await fs.writeFile(vttPath, vtt); // Generate SRT format const srtPath = path.join(this.config.outputPath, tutorial.id, `${tutorial.id}.srt`); let srt = ''; let index = 1; for (const caption of tutorial.script.captions) { const start = this.formatTimeSRT(caption.timestamp); const end = this.formatTimeSRT(caption.timestamp + caption.duration); srt += `${index}\n${start} --> ${end}\n${caption.text}\n\n`; index++; } await fs.writeFile(srtPath, srt); } async generateInteractiveGuide(guide) { const guidePath = path.join(this.config.outputPath, 'guides', guide.id); await fs.ensureDir(guidePath); // Generate guide HTML const html = this.generateGuideHTML(guide); await fs.writeFile(path.join(guidePath, 'index.html'), html); // Generate guide JavaScript const js = this.generateGuideJS(guide); await fs.writeFile(path.join(guidePath, 'guide.js'), js); // Generate guide CSS const css = this.generateGuideCSS(); await fs.writeFile(path.join(guidePath, 'guide.css'), css); // Generate guide data await fs.writeJson(path.join(guidePath, 'guide-data.json'), guide, { spaces: 2 }); } generateGuideHTML(guide) { return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>${guide.title} - Re-Shell Interactive Guide</title> <link rel="stylesheet" href="guide.css"> </head> <body> <div class="guide-container"> <header class="guide-header"> <h1>${guide.title}</h1> <p>${guide.description}</p> <div class="guide-meta"> <span class="difficulty difficulty-${guide.difficulty}">${guide.difficulty}</span> <span class="time">⏱️ ${guide.estimatedTime} minutes</span> </div> </header> <div class="progress-bar"> <div class="progress-fill" id="progress"></div> </div> <main class="guide-content" id="content"> <!-- Dynamic content will be loaded here --> </main> <nav class="guide-navigation"> <button id="prev-btn" class="nav-btn" disabled>Previous</button> <span class="step-indicator" id="step-indicator">Step 1 of ${guide.steps.length}</span> <button id="next-btn" class="nav-btn">Next</button> </nav> <aside class="achievements" id="achievements"> <h3>Achievements</h3> <div class="achievement-list"> ${guide.achievements.map(a => ` <div class="achievement" data-id="${a.id}"> <span class="achievement-icon">${a.icon}</span> <span class="achievement-title">${a.title}</span> </div> `).join('')} </div> </aside> </div> <script src="guide.js"></script> <script> window.guideData = ${JSON.stringify(guide)}; initializeGuide(window.guideData); </script> </body> </html>`; } generateGuideJS(guide) { return `// Interactive Guide JavaScript let currentStep = 0; let startTime = Date.now(); let achievements = new Set(); let hintsUsed = 0; function initializeGuide(guideData) { window.guide = guideData; loadStep(0); document.getElementById('prev-btn').addEventListener('click', previousStep); document.getElementById('next-btn').addEventListener('click', nextStep); // Keyboard navigation document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft') previousStep(); if (e.key === 'ArrowRight') nextStep(); }); } function loadStep(stepIndex) { const step = guide.steps[stepIndex]; const content = document.getElementById('content'); content.innerHTML = \` <div class="step"> <h2>\${step.title}</h2> <p>\${step.description}</p> <div class="step-content"> \${renderStepContent(step.content)} </div> \${step.hints ? \` <div class="hints"> <button onclick="showHint()">💡 Need a hint?</button> <div id="hint-text" style="display: none;"></div> </div> \` : ''} \${step.validation ? \` <div class="validation"> <button onclick="validateStep()">Check Answer</button> <div id="validation-result"></div> </div> \` : ''} </div> \`; updateProgress(); updateNavigation(); checkAchievements(); } function renderStepContent(content) { let html = ''; if (content.text) { html += \`<p>\${content.text}</p>\`; } if (content.code) { html += \`<pre><code>\${escapeHtml(content.code)}</code></pre>\`; } if (content.command) { html += \` <div class="command-box"> <code>\${content.command}</code> <button onclick="copyCommand('\${content.command}')">📋 Copy</button> </div> \`; } if (content.interactive) { html += renderInteractiveElement(content.interactive); } return html; } function renderInteractiveElement(element) { switch (element.type) { case 'terminal': return \` <div class="terminal"> <div class="terminal-header">Terminal</div> <div class="terminal-body" id="terminal-output"> <div class="terminal-prompt">$ <input type="text" id="terminal-input" placeholder="Type command here..."></div> </div> </div> \`; case 'editor': return \` <div class="code-editor"> <div class="editor-header">Code Editor</div> <textarea class="editor-body" id="code-editor" rows="10"></textarea> </div> \`; default: return ''; } } function validateStep() { const step = guide.steps[currentStep]; if (!step.validation) return; let userInput = ''; // Get user input based on step type if (step.content.interactive?.type === 'terminal') { userInput = document.getElementById('terminal-input')?.value || ''; } else if (step.content.interactive?.type === 'editor') { userInput = document.getElementById('code-editor')?.value || ''; } const isValid = checkValidation(userInput, step.validation); const resultDiv = document.getElementById('validation-result'); if (isValid) { resultDiv.innerHTML = \`<div class="success">\${step.validation.successMessage || 'Correct!'}</div>\`; setTimeout(() => nextStep(), 1500); } else { resultDiv.innerHTML = \`<div class="error">\${step.validation.errorMessage || 'Try again!'}</div>\`; } } function checkValidation(input, validation) { switch (validation.type) { case 'exact': return input === validation.value; case 'contains': return input.includes(validation.value); case 'regex': return new RegExp(validation.value).test(input); default: return false; } } function previousStep() { if (currentStep > 0) { currentStep--; loadStep(currentStep); } } function nextStep() { if (currentStep < guide.steps.length - 1) { currentStep++; loadStep(currentStep); } else { completeGuide(); } } function updateProgress() { const progress = ((currentStep + 1) / guide.steps.length) * 100; document.getElementById('progress').style.width = progress + '%'; document.getElementById('step-indicator').textContent = \`Step \${currentStep + 1} of \${guide.steps.length}\`; } function updateNavigation() { document.getElementById('prev-btn').disabled = currentStep === 0; document.getElementById('next-btn').textContent = currentStep === guide.steps.length - 1 ? 'Complete' : 'Next'; } function showHint() { const step = guide.steps[currentStep]; if (step.hints && step.hints.length > 0) { const hintText = document.getElementById('hint-text'); hintText.textContent = step.hints[0]; hintText.style.display = 'block'; hintsUsed++; } } function copyCommand(command) { navigator.clipboard.writeText(command); // Show feedback event.target.textContent = '✅ Copied!'; setTimeout(() => { event.target.textContent = '📋 Copy'; }, 2000); } function checkAchievements() { const elapsed = Date.now() - startTime; guide.achievements.forEach(achievement => { if (achievements.has(achievement.id)) return; let earned = false; switch (achievement.condition.type) { case 'complete_guide': earned = currentStep === guide.steps.length - 1; break; case 'speed_run': earned = currentStep === guide.steps.length - 1 && elapsed < achievement.condition.value * 1000; break; case 'no_hints': earned = currentStep === guide.steps.length - 1 && hintsUsed === 0; break; } if (earned) { achievements.add(achievement.id); showAchievement(achievement); } }); } function showAchievement(achievement) { const element = document.querySelector(\`[data-id="\${achievement.id}"]\`); if (element) { element.classList.add('earned'); } // Show notification const notification = document.createElement('div'); notification.className = 'achievement-notification'; notification.innerHTML = \` <span class="icon">\${achievement.icon}</span> <div> <strong>\${achievement.title}</strong> <p>\${achievement.description}</p> </div> \`; document.body.appendChild(notification); setTimeout(() => notification.remove(), 5000); } function completeGuide() { checkAchievements(); const content = document.getElementById('content'); content.innerHTML = \` <div class="completion"> <h2>🎉 Congratulations!</h2> <p>You've completed the ${guide.title}!</p> <div class="stats"> <p>Time: \${Math.floor((Date.now() - startTime) / 60000)} minutes</p> <p>Hints used: \${hintsUsed}</p> <p>Achievements earned: \${achievements.size}</p> </div> <button onclick="location.reload()">Start Again</button> </div> \`; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }`; } generateGuideCSS() { return `/* Interactive Guide Styles */ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 0; background: #f5f5f5; } .guide-container { max-width: 1200px; margin: 0 auto; padding: 20px; } .guide-header { background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin-bottom: 20px; } .guide-header h1 { margin: 0 0 10px 0; color: #333; } .guide-meta { display: flex; gap: 20px; margin-top: 15px; } .difficulty { padding: 5px 15px; border-radius: 20px; font-size: 14px; font-weight: 500; } .difficulty-beginner { background: #4caf50; color: white; } .difficulty-intermediate { background: #ff9800; color: white; } .difficulty-advanced { background: #f44336; color: white; } .progress-bar { height: 6px; background: #e0e0e0; border-radius: 3px; margin-bottom: 20px; overflow: hidden; } .progress-fill { height: 100%; background: #2196f3; transition: width 0.3s ease; } .guide-content { background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); min-height: 400px; } .step h2 { color: #333; margin-bottom: 10px; } .step-content { margin: 20px 0; } pre { background: #f4f4f4; padding: 15px; border-radius: 5px; overflow-x: auto; } code { font-family: 'Consolas', 'Monaco', 'Courier New', monospace; } .command-box { display: flex; align-items: center; background: #f4f4f4; padding: 10px 15px; border-radius: 5px; margin: 10px 0; } .command-box code { flex: 1; } .command-box button { background: #2196f3; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; margin-left: 10px; } .terminal { background: #1e1e1e; border-radius: 5px; overflow: hidden; margin: 20px 0; } .terminal-header { background: #333; color: white; padding: 8px 15px; font-size: 14px; } .terminal-body { padding: 15px; color: #0f0; font-family: monospace; min-height: 150px; } .terminal-prompt input { background: transparent; border: none; color: #0f0; font-family: monospace; width: calc(100% - 20px); outline: none; } .code-editor { border: 1px solid #ddd; border-radius: 5px; overflow: hidden; margin: 20px 0; } .editor-header { background: #f5f5f5; padding: 8px 15px; border-bottom: 1px solid #ddd; font-size: 14px; } .editor-body { width: 100%; border: none; padding: 15px; font-family: monospace; font-size: 14px; resize: vertical; } .hints { margin: 20px 0; } .hints button { background: #ffc107; color: #333; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer; } #hint-text { background: #fff9c4; padding: 10px; border-radius: 5px; margin-top: 10px; } .validation { margin: 20px 0; } .validation button { background: #4caf50; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 16px; } .success { color: #4caf50; padding: 10px; margin-top: 10px; } .error { color: #f44336; padding: 10px; margin-top: 10px; } .guide-navigation { display: flex; justify-content: space-between; align-items: center; margin-top: 20px; padding: 20px; background: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .nav-btn { background: #2196f3; color: white; border: none; padding: 10px 25px; border-radius: 5px; cursor: pointer; font-size: 16px; transition: background 0.2s; } .nav-btn:hover:not(:disabled) { background: #1976d2; } .nav-btn:disabled { background: #ccc; cursor: not-allowed; } .achievements { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin-top: 20px; } .achievement-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; margin-top: 15px; } .achievement { display: flex; align-items: center; gap: 10px; padding: 10px; background: #f5f5f5; border-radius: 5px; opacity: 0.5; transition: all 0.3s; } .achievement.earned { opacity: 1; background: #e8f5e9; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .achievement-icon { font-size: 24px; } .achievement-notification { position: fixed; top: 20px; right: 20px; background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); display: flex; align-items: center; gap: 15px; animation: slideIn 0.3s ease; } @keyframes slideIn { from { transform: translateX(100%); } to { transform: translateX(0); } } .completion { text-align: center; padding: 40px; } .completion h2 { font-size: 48px; margin-bottom: 20px; } .stats { background: #f5f5f5; padding: 20px; border-radius: 10px; margin: 30px 0; } .stats p { margin: 5px 0; font-size: 18px; }`; } async generateTutorialIndex() { const indexPath = path.join(this.config.outputPath, 'index.html'); const html = `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Re-Shell Video Tutorials</title> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; } .container { max-width: 1200px; margin: 0 auto; } h1 { color: #333; margin-bottom: 30px; } .tutorial-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 20px; } .tutorial-card { background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 2px 10px rgba(0,0,0,0.1); transition: transform 0.2s; } .tutorial-card:hover { transform: translateY(-5px); } .tutorial-thumbnail { width: 100%; height: 200px; background: #333; display: flex; align-items: center; justify-content: center; color: white; font-size: 24px; } .tutorial-info { padding: 20px; } .tutorial-title { font-size: 20px; font-weight: 600; margin-bottom: 10px; color: #333; } .tutorial-meta { display: flex; gap: 15px; color: #666; font-size: 14px; } .watch-btn { display: block; width: 100%; padding: 10px; background: #2196f3; color: white; text-align: center; text-decoration: none; border-radius: 5px; margin-top: 15px; } .guides-section { margin-top: 50px; } .guide-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 15px; margin-top: 20px; } .guide-item { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); text-decoration: none; color: #333; transition: transform 0.2s; } .guide-item:hover { transform: translateY(-3px); } </style> </head> <body> <div class="container"> <h1>Re-Shell Video Tutorials</h1> <div class="tutorial-grid"> ${Array.from(this.tutorials.values()).map(tutorial => ` <div class="tutorial-card"> <div class="tutorial-thumbnail">▶️ ${tutorial.duration / 60}min</div> <div class="tutorial-info"> <div class="tutorial-title">${tutorial.title}</div> <p>${tutorial.description}</p> <div class="tutorial-meta"> <span>📊 ${tutorial.difficulty}</span> <span>⏱️ ${Math.floor(tutorial.duration / 60)} minutes</span> </div> <a href="${tutorial.id}/index.html" class="watch-btn">Watch Tutorial</a> </div> </div> `).join('')} </div> <div class="guides-section"> <h2>Interactive Guides</h2> <div class="guide-list"> ${Array.from(this.guides.values()).map(guide => ` <a href="guides/${guide.id}/index.html" class="guide-item"> <h3>${guide.title}</h3> <p>${guide.description}</p> <div class="tutorial-meta"> <span>📊 ${guide.difficulty}</span> <span>⏱️ ${guide.estimatedTime} minutes</span> </div> </a> `).join('')} </div> </div> </div> </body> </html>`; await fs.writeFile(indexPath, html); } async generateLearningPlatform() { const platformPath = path.join(this.config.outputPath, 'platform'); await fs.ensureDir(platformPath); // Create learning dashboard const dashboardHtml = `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Re-Shell Learning Platform</title> <style> /* Platform styles would go here */ </style> </head> <body> <div class="learning-platform"> <header> <h1>Re-Shell Learning Platform</h1> <nav> <a href="#tutorials">Tutorials</a> <a href="#guides">Interactive Guides</a> <a href="#progress">My Progress</a> <a href="#achievements">Achievements</a> </nav> </header> <main> <section id="learning-paths"> <h2>Learning Paths</h2> <div class="path-grid"> <div class="learning-path"> <h3>Beginner Path</h3> <p>Start your Re-Shell journey</p> <progress value="0" max="100"></progress> </div> <div class="learning-path"> <h3>Advanced Path</h3> <p>Master advanced features</p> <progress value="0" max="100"></progress> </div> </div> </section> </main> </div> </body> </html>`; await fs.writeFile(path.join(platformPath, 'index.html'), dashboardHtml); } async publishToTarget(target) {