// Tutorial-specific JavaScript functionality let currentTutorial = null; let currentStep = 0; let totalSteps = 0; /** * Initialize tutorial functionality */ function initializeTutorial(tutorialData) { currentTutorial = tutorialData; currentStep = tutorialData.currentStep || 0; totalSteps = tutorialData.totalSteps; setupTutorialNavigation(); setupStepTracking(); setupKeyboardNavigation(); updateProgress(); // Show the current step showStep(currentStep); } /** * Setup tutorial navigation */ function setupTutorialNavigation() { // Step navigation links document.querySelectorAll('.step-nav-link').forEach(link => { link.addEventListener('click', function(e) { e.preventDefault(); const stepIndex = parseInt(this.dataset.step); navigateToStep(stepIndex); }); }); // Next/Previous buttons document.querySelectorAll('[onclick*="navigateToStep"]').forEach(btn => { btn.addEventListener('click', function(e) { e.preventDefault(); const match = this.getAttribute('onclick').match(/navigateToStep\((\d+)\)/); if (match) { navigateToStep(parseInt(match[1])); } }); }); } /** * Setup step tracking */ function setupStepTracking() { // Track time spent on each step let stepStartTime = Date.now(); window.addEventListener('beforeunload', () => { const timeSpent = Math.floor((Date.now() - stepStartTime) / 1000); trackStepTime(currentStep, timeSpent); }); // Track step completion document.addEventListener('visibilitychange', () => { if (document.hidden) { const timeSpent = Math.floor((Date.now() - stepStartTime) / 1000); trackStepTime(currentStep, timeSpent); } else { stepStartTime = Date.now(); } }); } /** * Setup keyboard navigation */ function setupKeyboardNavigation() { document.addEventListener('keydown', function(e) { // Only handle keyboard navigation when tutorial content is focused if (!document.querySelector('.tutorial-content').contains(document.activeElement)) { return; } switch(e.key) { case 'ArrowLeft': if (currentStep > 0) { e.preventDefault(); navigateToStep(currentStep - 1); } break; case 'ArrowRight': if (currentStep < totalSteps - 1) { e.preventDefault(); navigateToStep(currentStep + 1); } break; case 'Home': e.preventDefault(); navigateToStep(0); break; case 'End': e.preventDefault(); navigateToStep(totalSteps - 1); break; } }); } /** * Navigate to a specific step */ function navigateToStep(stepIndex) { if (stepIndex < 0 || stepIndex >= totalSteps) { return; } // Hide current step const currentStepElement = document.querySelector(`#step-${currentStep}`); if (currentStepElement) { currentStepElement.classList.remove('active'); } // Update step navigation document.querySelectorAll('.step-nav-link').forEach(link => { link.classList.remove('active'); }); // Show new step currentStep = stepIndex; showStep(currentStep); // Update navigation const newNavLink = document.querySelector(`[data-step="${currentStep}"]`); if (newNavLink) { newNavLink.classList.add('active'); } // Update progress updateProgress(); // Save progress saveProgress(); // Track step view window.DifyLearning?.trackEvent('tutorial_step_viewed', { tutorial_id: currentTutorial.id, step: currentStep + 1, step_title: document.querySelector(`#step-${currentStep} .step-title`)?.textContent }); // Scroll to top of content const tutorialContent = document.querySelector('.tutorial-content'); if (tutorialContent) { tutorialContent.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } /** * Show a specific step */ function showStep(stepIndex) { // Hide all steps document.querySelectorAll('.tutorial-step').forEach(step => { step.classList.remove('active'); }); // Show target step const targetStep = document.querySelector(`#step-${stepIndex}`); if (targetStep) { targetStep.classList.add('active'); // Initialize any interactive elements in this step initializeStepElements(targetStep); // Add fade-in animation targetStep.classList.add('fade-in'); // Update step counter updateStepCounter(stepIndex + 1); } } /** * Initialize interactive elements in a step */ function initializeStepElements(stepElement) { // Initialize code examples with syntax highlighting const codeBlocks = stepElement.querySelectorAll('pre code'); codeBlocks.forEach(block => { // Add copy button addCopyButton(block); // Add line numbers if needed if (block.textContent.split('\n').length > 5) { addLineNumbers(block); } }); // Initialize interactive demos const demos = stepElement.querySelectorAll('.interactive-demo'); demos.forEach(demo => { initializeDemo(demo); }); // Initialize tooltips for this step const tooltips = stepElement.querySelectorAll('[data-bs-toggle="tooltip"]'); tooltips.forEach(tooltip => { new bootstrap.Tooltip(tooltip); }); } /** * Add copy button to code blocks */ function addCopyButton(codeBlock) { if (codeBlock.querySelector('.copy-button')) { return; // Already has copy button } const copyButton = document.createElement('button'); copyButton.className = 'btn btn-sm btn-outline-secondary copy-button position-absolute top-0 end-0 m-2'; copyButton.innerHTML = ''; copyButton.title = 'Copy code'; copyButton.addEventListener('click', async () => { try { await navigator.clipboard.writeText(codeBlock.textContent); copyButton.innerHTML = ''; copyButton.classList.add('btn-success'); copyButton.classList.remove('btn-outline-secondary'); setTimeout(() => { copyButton.innerHTML = ''; copyButton.classList.remove('btn-success'); copyButton.classList.add('btn-outline-secondary'); feather.replace(); }, 2000); window.DifyLearning?.showToast('Code copied to clipboard!', 'success'); } catch (err) { window.DifyLearning?.showToast('Failed to copy code', 'danger'); } }); // Make parent relative positioned const pre = codeBlock.closest('pre'); if (pre) { pre.style.position = 'relative'; pre.appendChild(copyButton); feather.replace(); } } /** * Add line numbers to code blocks */ function addLineNumbers(codeBlock) { const lines = codeBlock.textContent.split('\n'); const lineNumbers = lines.map((_, index) => index + 1).join('\n'); const lineNumbersElement = document.createElement('pre'); lineNumbersElement.className = 'line-numbers'; lineNumbersElement.textContent = lineNumbers; lineNumbersElement.style.cssText = ` position: absolute; left: 0; top: 0; padding: 1rem 0.5rem; background: #f1f3f4; color: #666; font-size: 0.8rem; border-right: 1px solid #ddd; user-select: none; width: 3rem; text-align: right; `; const pre = codeBlock.closest('pre'); if (pre) { pre.style.position = 'relative'; pre.style.paddingLeft = '4rem'; pre.appendChild(lineNumbersElement); } } /** * Initialize interactive demos */ function initializeDemo(demoElement) { const demoType = demoElement.dataset.demoType; switch(demoType) { case 'workflow-builder': initializeWorkflowDemo(demoElement); break; case 'chatbot-preview': initializeChatbotDemo(demoElement); break; case 'agent-configuration': initializeAgentDemo(demoElement); break; default: // Generic demo initialization demoElement.innerHTML = `
Interactive Demo