@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
JavaScript
"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) {