UNPKG

freshroute-server

Version:

Local development server for FreshRoute extension with API mocking and extended functionality

1,110 lines (970 loc) โ€ข 54.2 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FreshRoute Setup - Welcome</title> <link rel="icon" href="./extension/icons/icon48.png" type="image/png"> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; line-height: 1.6; color: #333; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .container { max-width: 900px; margin: 0 auto; background: white; border-radius: 20px; box-shadow: 0 20px 40px rgba(0,0,0,0.1); overflow: hidden; } .header { background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; padding: 40px; text-align: center; } .header-icon { width: 64px; height: 64px; margin: 0 auto 20px; display: block; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); } .header h1 { font-size: 2.5em; margin-bottom: 10px; font-weight: 300; } .header p { font-size: 1.2em; opacity: 0.9; } .content { padding: 40px; } .status-card { background: #f8f9fa; border-radius: 12px; padding: 20px; margin-bottom: 30px; border-left: 4px solid #4CAF50; } .status-card.warning { border-left-color: #ff9800; background: #fff3e0; } .status-card.error { border-left-color: #f44336; background: #ffebee; } .chrome-dev-notice { background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%); color: white; border-radius: 12px; padding: 20px; margin-bottom: 30px; text-align: center; } .chrome-dev-notice h3 { margin-bottom: 10px; font-size: 1.3em; } .chrome-dev-notice p { opacity: 0.9; margin-bottom: 15px; } .setup-steps { display: grid; gap: 20px; margin: 30px 0; } .step { background: white; border: 2px solid #e0e0e0; border-radius: 12px; padding: 25px; transition: all 0.3s ease; } .step:hover { border-color: #4CAF50; transform: translateY(-2px); box-shadow: 0 8px 25px rgba(0,0,0,0.1); } .step-number { background: #4CAF50; color: white; width: 30px; height: 30px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: bold; margin-right: 15px; } .step-title { font-size: 1.3em; font-weight: 600; margin-bottom: 10px; display: flex; align-items: center; } .step-content { margin-left: 45px; color: #666; } .code-block { background: #2d3748; color: #e2e8f0; padding: 15px; border-radius: 8px; font-family: 'Monaco', 'Menlo', monospace; font-size: 0.9em; margin: 10px 0; overflow-x: auto; position: relative; } .copy-btn { position: absolute; top: 10px; right: 10px; background: #4CAF50; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; font-size: 0.8em; } .copy-btn:hover { background: #45a049; } .button { display: inline-block; background: #4CAF50; color: white; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600; transition: all 0.3s ease; margin: 5px; border: none; cursor: pointer; } .button:hover { background: #45a049; transform: translateY(-1px); } .button.secondary { background: #6c757d; } .button.secondary:hover { background: #5a6268; } .button.danger { background: #dc3545; } .button.danger:hover { background: #c82333; } .quick-links { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin: 30px 0; } .quick-link { background: #f8f9fa; padding: 20px; border-radius: 12px; text-align: center; text-decoration: none; color: #333; transition: all 0.3s ease; cursor: pointer; } .quick-link:hover { background: #e9ecef; transform: translateY(-2px); } .icon { font-size: 2em; margin-bottom: 10px; display: block; } .footer { background: #f8f9fa; padding: 30px; text-align: center; color: #666; } .chrome-managed { background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 8px; padding: 15px; margin: 15px 0; } .chrome-managed h4 { color: #856404; margin-bottom: 10px; } .tip-box { background: #e3f2fd; border-left: 4px solid #2196f3; padding: 15px; margin: 15px 0; border-radius: 4px; } .tip-box h4 { color: #1976d2; margin-bottom: 8px; } .extension-status { padding: 10px; border-radius: 8px; margin: 10px 0; font-weight: 500; } .extension-status.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; } .extension-status.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } .extension-status.warning { background: #fff3cd; color: #856404; border: 1px solid #ffeaa7; } .modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); } .modal-content { background-color: white; margin: 5% auto; padding: 30px; border-radius: 12px; width: 80%; max-width: 600px; max-height: 80vh; overflow-y: auto; } .close { color: #aaa; float: right; font-size: 28px; font-weight: bold; cursor: pointer; } .close:hover { color: black; } .collapsible-content { display: none; margin-top: 15px; padding: 15px; border-radius: 8px; } .instruction-panel { background: #fff3e0; border-left: 4px solid #ff9800; } .manual-steps-panel { background: #f8f9fa; } </style> </head> <body> <div class="container"> <div class="header"> <img src="./extension/icons/icon128.png" alt="FreshRoute" class="header-icon"> <h1>FreshRoute Setup</h1> <p>API Mocking & URL Rewriting Made Easy</p> </div> <div class="content"> <div class="status-card" id="welcomeMessage"> <h3>๐Ÿ‘‹ Welcome to FreshRoute!</h3> <p>Let's get you set up quickly. We'll automatically detect your extension status and guide you through any needed steps.</p> <div style="margin-top: 15px;"> <div id="autoCheckStatus" class="extension-status warning"> ๐Ÿ”„ <strong>Checking for FreshRoute extension...</strong> <br><small>Please wait while we detect your extension status...</small> </div> </div> </div> <div class="chrome-dev-notice" id="chromeDevNotice" style="display: none;"> <h3>๐Ÿ”ด Development Chrome Active</h3> <p>Perfect! You're using Chrome with CORS disabled. Extensions need to be manually loaded in this mode.</p> </div> <div class="status-card warning" id="serverStatus"> <h3>โš ๏ธ Server Status: Not Required for Setup</h3> <p>The FreshRoute server is <strong>not required</strong> for viewing this setup guide.</p> <p>You only need to start the server when you want to use <strong>API mocking</strong> or <strong>response header overrides</strong>.</p> <div style="margin-top: 15px;"> <button class="button" onclick="ServerManager.checkStatus()">๐Ÿ” Check Server Status</button> <button class="button secondary" onclick="GuideManager.show('server-instructions')">๐Ÿ“– Server Instructions</button> </div> </div> <div class="setup-steps"> <div class="step" id="extensionStep"> <div class="step-title"> <span class="step-number">1</span> Chrome Extension Setup </div> <div class="step-content"> <div id="extensionStatus"> <div class="extension-status warning"> ๐Ÿ”„ <strong>Checking extension status...</strong> <br><small>We'll automatically detect your extension and guide you through setup.</small> </div> </div> <!-- Installation instructions (hidden by default) --> <div id="installationInstructions" style="display: none;"> <div style="margin: 15px 0;"> <button class="button" onclick="GuideManager.toggleSection('chromeExtensionsInstructions')">๐Ÿ”ง How to Open Chrome Extensions</button> <button class="button secondary" onclick="GuideManager.toggleSection('manualSteps')">๐Ÿ“‹ Step-by-Step Guide</button> <button class="button secondary" onclick="Utils.copyExtensionPath()">๐Ÿ“ Copy Extension Path</button> </div> <div id="chromeExtensionsInstructions" class="collapsible-content instruction-panel"> <h4>๐Ÿ”ง How to Open Chrome Extensions Page:</h4> <p style="margin: 10px 0;"><strong>Method 1:</strong> Type in address bar:</p> <div class="code-block"> <span id="chromeExtensionsUrl">chrome://extensions/</span> <button class="copy-btn" onclick="Utils.copyText('chromeExtensionsUrl', this)">Copy</button> </div> <p style="margin: 10px 0;"><strong>Method 2:</strong> Use Chrome menu โ†’ More tools โ†’ Extensions</p> </div> <div id="manualSteps" class="collapsible-content manual-steps-panel"> <h4>๐Ÿ“‹ Manual Installation Steps:</h4> <ol style="margin: 10px 0; padding-left: 20px; line-height: 1.8;"> <li>Open Chrome Extensions page (see instructions above)</li> <li>Enable <strong>"Developer mode"</strong> (toggle in top-right corner)</li> <li>Click <strong>"Load unpacked"</strong> button</li> <li>Navigate to the extension folder: <div class="code-block"> <span id="extensionPath">~/.freshroute/extension</span> <button class="copy-btn" onclick="Utils.copyText('extensionPath', this)">Copy</button> </div> </li> <li>Select the folder and click <strong>"Select Folder"</strong></li> <li>The FreshRoute extension should now appear in your extensions list</li> </ol> </div> </div> <div style="margin: 15px 0;"> <button class="button secondary" onclick="ExtensionManager.checkStatus()">๐Ÿ”„ Check Again</button> <button class="button secondary" onclick="ExtensionManager.testConnection()">๐Ÿงช Test Connection</button> </div> </div> </div> <div class="step"> <div class="step-title"> <span class="step-number">2</span> Start FreshRoute Server (Optional) </div> <div class="step-content"> <p>The server is only needed for <strong>API mocking</strong> and <strong>response header overrides</strong>. URL rewriting works without it!</p> <div class="tip-box"> <h4>๐Ÿ’ก When do you need the server?</h4> <ul style="margin: 5px 0; padding-left: 20px;"> <li><strong>API Mocking:</strong> Replace real API responses with custom mock data</li> <li><strong>Response Headers:</strong> Add custom headers (only headers starting with 'x-' can be added without server)</li> <li><strong>Dashboard Access:</strong> Create and manage mock rules via web interface</li> </ul> </div> <h4>๐Ÿš€ Starting the Server:</h4> <div class="code-block"> <span>freshroute start --silent</span> <button class="copy-btn" onclick="Utils.copyText('startCommand', this)">Copy</button> </div> <span id="startCommand" style="display: none;">freshroute start --silent</span> <p style="margin: 5px 0; font-size: 0.9em; color: #666;">Runs in background, doesn't block your terminal</p> <div style="margin: 15px 0;"> <button class="button" onclick="ServerManager.checkStatus()">๐Ÿ” Check Server Status</button> <button class="button secondary" onclick="GuideManager.show('server-commands')">๐Ÿ“‹ Server Commands</button> </div> </div> </div> <div class="step"> <div class="step-title"> <span class="step-number">3</span> Start Using FreshRoute </div> <div class="step-content"> <p>Access the dashboard and extension features to create mock rules and manage API responses.</p> <div style="margin: 15px 0;"> <button class="button secondary" onclick="GuideManager.show('extension-controls')">๐ŸŽ›๏ธ Extension Controls</button> <button class="button secondary" onclick="GuideManager.show('usage-guide')">๐Ÿ“– Usage Guide</button> </div> </div> </div> </div> <div class="quick-links"> <div class="quick-link" onclick="GuideManager.show('network-recorder')"> <span class="icon">๐Ÿ“ก</span> <h3>Network Recorder</h3> <p>Capture real requests to create mocks</p> </div> <div class="quick-link" onclick="GuideManager.show('extension-guide')"> <span class="icon">โš™๏ธ</span> <h3>Extension Guide</h3> <p>Learn how to use the extension</p> </div> <div class="quick-link" onclick="GuideManager.show('allowed-domains')"> <span class="icon">๐Ÿ”’</span> <h3>Allowed Domains</h3> <p>View permitted development domains</p> </div> <div class="quick-link" onclick="GuideManager.show('troubleshooting')"> <span class="icon">๐Ÿ”ง</span> <h3>Troubleshooting</h3> <p>Fix common issues and problems</p> </div> </div> </div> <div class="footer"> <p><strong>FreshRoute v2.0.0</strong> - API Mocking & URL Rewriting Tool</p> <p>Server available at <code>http://localhost:3001</code> (when running)</p> <p style="margin-top: 10px; font-size: 0.9em; opacity: 0.8;"> ๐Ÿ’ก <strong>Tip:</strong> Keep this Chrome window open while developing to maintain CORS-disabled mode </p> <p style="margin-top: 5px; font-size: 0.9em; opacity: 0.8;"> ๐Ÿš€ <strong>Server:</strong> Only needed for API mocking and response header overrides </p> </div> </div> <!-- Modal for guides and help --> <div id="helpModal" class="modal"> <div class="modal-content"> <span class="close" onclick="GuideManager.closeModal()">&times;</span> <div id="modalContent"></div> </div> </div> <script> // Utility functions const Utils = { copyText: async function(elementId, buttonElement = null) { const element = document.getElementById(elementId); const text = element.textContent; if (!buttonElement) { const codeBlock = element.closest('.code-block'); if (codeBlock) { buttonElement = codeBlock.querySelector('.copy-btn'); } } try { if (navigator.clipboard) { await navigator.clipboard.writeText(text); } else { this.fallbackCopy(text); } this.showCopyFeedback(buttonElement); } catch (error) { this.fallbackCopy(text); this.showCopyFeedback(buttonElement); } }, fallbackCopy: function(text) { const textArea = document.createElement('textarea'); textArea.value = text; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); }, showCopyFeedback: function(buttonElement) { if (!buttonElement) return; const originalText = buttonElement.textContent; const originalColor = buttonElement.style.backgroundColor; buttonElement.textContent = 'โœ“ Copied!'; buttonElement.style.backgroundColor = '#4CAF50'; buttonElement.style.color = 'white'; setTimeout(() => { buttonElement.textContent = originalText; buttonElement.style.backgroundColor = originalColor; buttonElement.style.color = ''; }, 2000); }, copyExtensionPath: function() { this.copyText('extensionPath', event.target); }, updateStatus: function(elementId, content, className = '') { const element = document.getElementById(elementId); if (element) { element.innerHTML = content; if (className) { element.className = className; } } }, scrollToElement: function(selector, behavior = 'smooth') { const element = document.querySelector(selector); if (element) { element.scrollIntoView({ behavior, block: 'center' }); } } }; // Server management const ServerManager = { checkStatus: function() { const statusDiv = document.getElementById('serverStatus'); statusDiv.innerHTML = '<div class="status-card warning"><h3>๐Ÿ” Checking server status...</h3></div>'; fetch('http://localhost:3001/health') .then(response => response.json()) .then(data => { statusDiv.innerHTML = ` <div class="status-card"> <h3>โœ… Server Status: Running</h3> <p>FreshRoute server is running on <strong>http://localhost:3001</strong></p> <div style="margin-top: 15px;"> <button class="button secondary" onclick="GuideManager.show('server-commands')">๐Ÿ“‹ Server Commands</button> </div> </div>`; }) .catch(() => { statusDiv.innerHTML = ` <div class="status-card error"> <h3>โŒ Server Status: Not Running</h3> <p>The FreshRoute server is not currently running.</p> <p>Start it with: <code>freshroute start</code></p> <div style="margin-top: 15px;"> <button class="button" onclick="GuideManager.show('server-instructions')">๐Ÿ“– How to Start Server</button> <button class="button secondary" onclick="ServerManager.checkStatus()">๐Ÿ”„ Check Again</button> </div> </div>`; }); } }; // Extension management const ExtensionManager = { checkStatus: function() { const statusDiv = document.getElementById('extensionStatus') || document.getElementById('autoCheckStatus'); Utils.updateStatus(statusDiv.id, '๐Ÿ” <strong>Checking extension status...</strong><br><small>Scanning for installed extension...</small>', 'extension-status warning'); // Check for extension by looking for injected DOM elements this.checkForExtensionElements((extensionData) => { statusDiv.innerHTML = ` โœ… <strong>Extension detected and working!</strong> <br><small>Status: ${extensionData.enabled ? 'Enabled' : 'Disabled'} | Inactivity Timer: ${extensionData.inactivityTimeout}min</small> <br><small>Floating Toggle: ${extensionData.hasFloatingToggle ? 'Active' : 'Not visible'}</small>`; statusDiv.className = 'extension-status success'; StepManager.markInstallationComplete(); WelcomeManager.updateStatus('success'); document.getElementById('installationInstructions').style.display = 'none'; }, () => { statusDiv.innerHTML = ` โŒ <strong>Extension not found</strong> <br><small>Please install the Chrome extension to continue.</small>`; statusDiv.className = 'extension-status error'; StepManager.highlightInstallation(); WelcomeManager.updateStatus('error'); document.getElementById('installationInstructions').style.display = 'block'; }); }, checkForExtensionElements: function(onSuccess, onError, timeout = 5000) { let attempts = 0; const maxAttempts = 10; const checkInterval = 200; // Check every 200ms const checkForElements = () => { attempts++; // Check for floating toggle (most reliable indicator) const floatingToggle = document.getElementById('freshroute-floating-toggle'); // Check for notification styles (another indicator) const notificationStyles = document.querySelector('style[data-freshroute-notifications]'); // Check for any FreshRoute-related elements const freshRouteElements = document.querySelectorAll('[id*="freshroute"], [class*="freshroute"]'); // Check for global FreshRoute objects const hasGlobalObjects = window.FreshRouteNotifications || window.debugExtensionHeaders; if (floatingToggle || notificationStyles || freshRouteElements.length > 0 || hasGlobalObjects) { // Extension is present and active const extensionData = { hasFloatingToggle: !!floatingToggle, hasNotificationStyles: !!notificationStyles, elementsCount: freshRouteElements.length, hasGlobalObjects: !!hasGlobalObjects, enabled: floatingToggle ? !floatingToggle.textContent.includes('OFF') : true, inactivityTimeout: 30 // Default to 30 minutes }; // Try to get more details from floating toggle if (floatingToggle) { const toggleText = floatingToggle.textContent; extensionData.enabled = toggleText.includes('ON'); // Extract timer info if present const timerMatch = toggleText.match(/(\d+)m/); if (timerMatch) { extensionData.remainingTime = timerMatch[1]; } } onSuccess(extensionData); return; } // Continue checking if we haven't reached max attempts if (attempts < maxAttempts) { setTimeout(checkForElements, checkInterval); } else { onError(); } }; // Start checking immediately checkForElements(); }, testConnection: function() { const statusDiv = document.getElementById('extensionStatus'); Utils.updateStatus(statusDiv.id, '๐Ÿงช <strong>Testing extension functionality...</strong>', 'extension-status warning'); // Test extension by checking multiple indicators this.checkForExtensionElements((extensionData) => { // Additional functionality tests const hasFloatingToggle = extensionData.hasFloatingToggle; const hasGlobalFunctions = !!window.FreshRouteNotifications; const canAccessNotifications = hasGlobalFunctions && typeof window.FreshRouteNotifications.count === 'function'; let functionalityScore = 0; let functionalityDetails = []; if (hasFloatingToggle) { functionalityScore += 25; functionalityDetails.push('โœ… Floating toggle active'); } if (hasGlobalFunctions) { functionalityScore += 25; functionalityDetails.push('โœ… Global functions available'); } if (canAccessNotifications) { functionalityScore += 25; functionalityDetails.push('โœ… Notification system ready'); try { const notificationCount = window.FreshRouteNotifications.count(); functionalityDetails.push(`โœ… Active notifications: ${notificationCount}`); functionalityScore += 25; } catch (e) { functionalityDetails.push('โš ๏ธ Notification count unavailable'); } } if (extensionData.elementsCount > 0) { functionalityDetails.push(`โœ… Extension elements found: ${extensionData.elementsCount}`); } statusDiv.innerHTML = ` โœ… <strong>Extension functionality test: ${functionalityScore}% complete!</strong> <br><small>Extension is ${extensionData.enabled ? 'enabled' : 'disabled'} and functioning properly</small> <br><small style="margin-top: 5px; display: block;">${functionalityDetails.join('<br>')}</small>`; statusDiv.className = 'extension-status success'; }, () => { statusDiv.innerHTML = ` โŒ <strong>Extension functionality test failed</strong> <br><small>No extension elements detected. Please check installation.</small> <div style="margin-top: 15px;"> <button class="button secondary" onclick="GuideManager.show('troubleshooting')">๐Ÿ”ง Troubleshooting</button> </div>`; statusDiv.className = 'extension-status error'; }); }, getAllowedDomains: function(onSuccess, onError, timeout = 3000) { // Try to get domains from extension global objects first if (window.FRESHROUTE_ALLOWED_DOMAINS && Array.isArray(window.FRESHROUTE_ALLOWED_DOMAINS)) { console.log('โœ… Using allowed domains from global scope:', window.FRESHROUTE_ALLOWED_DOMAINS); onSuccess(window.FRESHROUTE_ALLOWED_DOMAINS); return; } // Try legacy global variable if (window.ALLOWED_DOMAINS && Array.isArray(window.ALLOWED_DOMAINS)) { console.log('โœ… Using allowed domains from legacy global scope:', window.ALLOWED_DOMAINS); onSuccess(window.ALLOWED_DOMAINS); return; } // Try DomainValidator if available if (window.DomainValidator && typeof window.DomainValidator.getAllowedDomains === 'function') { try { const domains = window.DomainValidator.getAllowedDomains(); console.log('โœ… Using allowed domains from DomainValidator:', domains); onSuccess(domains); return; } catch (error) { console.warn('โš ๏ธ Error using DomainValidator:', error); } } console.log('๐Ÿ”„ Falling back to message communication for allowed domains'); // Fallback to message communication this.communicateWithExtension('FRESHROUTE_GET_ALLOWED_DOMAINS', (data) => { if (data.allowedDomains && Array.isArray(data.allowedDomains)) { console.log('โœ… Received allowed domains via message:', data.allowedDomains); onSuccess(data.allowedDomains); } else { console.error('โŒ Invalid response from extension:', data); onError(); } }, onError, timeout); }, communicateWithExtension: function(messageType, onSuccess, onError, timeout = 3000) { let responseReceived = false; const messageListener = (event) => { if (event.data && event.data.type === `${messageType}_RESPONSE`) { responseReceived = true; onSuccess(event.data.data || {}); window.removeEventListener('message', messageListener); } }; window.addEventListener('message', messageListener); window.postMessage({ type: messageType, source: 'setup-page', timestamp: Date.now() }, '*'); setTimeout(() => { if (!responseReceived) { onError(); window.removeEventListener('message', messageListener); } }, timeout); } }; // Guide content management const GuideManager = { guides: { 'extension-controls': { title: '๐ŸŽ›๏ธ Extension Controls', content: ` <p>The FreshRoute extension provides several ways to control API mocking:</p> <h3>๐Ÿ“ Extension Icon</h3> <p>Click the FreshRoute icon in your browser toolbar to:</p> <ul> <li><strong>Enable/Disable</strong> - Toggle API mocking on/off</li> <li><strong>View Active Rules</strong> - See which rules are currently active</li> <li><strong>Quick Actions</strong> - Access common features quickly</li> </ul> ` }, 'usage-guide': { title: '๐Ÿ“– FreshRoute Usage Guide', content: ` <h3>๐Ÿš€ Quick Start</h3> <ol> <li><strong>Install Extension:</strong> Follow the installation steps above</li> <li><strong>Create Mock Rules:</strong> Use the Dashboard to define API responses</li> <li><strong>Browse Websites:</strong> FreshRoute automatically intercepts matching requests</li> <li><strong>Monitor Activity:</strong> Check the extension popup for active rules</li> </ol> ` }, 'server-commands': { title: '๐Ÿ“‹ FreshRoute Server Commands', content: ` <h3>๐Ÿš€ Starting & Stopping</h3> <div class="code-block"> <span>freshroute start --silent</span> <button class="copy-btn" onclick="Utils.copyText('cmdStart', this)">Copy</button> </div> <span id="cmdStart" style="display: none;">freshroute start --silent</span> <p>Start server in background (recommended)</p> ` }, 'allowed-domains': { title: '๐Ÿ”’ Allowed Development Domains', content: function() { return this.generateAllowedDomainsContent(); }, generateAllowedDomainsContent: function() { // Try to get domains from extension return new Promise((resolve) => { ExtensionManager.getAllowedDomains((domains) => { const domainsList = domains.map(domain => `<span style="display: inline-block; background: #e3f2fd; color: #1976d2; padding: 4px 8px; margin: 2px; border-radius: 4px; font-family: monospace; font-size: 0.9em;">${domain}</span>` ).join(''); const content = ` <div style="background: #fff3e0; border-left: 4px solid #ff9800; padding: 15px; margin: 15px 0; border-radius: 4px;"> <h4 style="color: #e65100; margin-bottom: 10px;">๐Ÿ”’ Domain Restrictions</h4> <p>FreshRoute URL rewriting is restricted to development domains only to prevent accidental production redirects.</p> </div> <h3>โœ… Currently Allowed Domains (${domains.length} total):</h3> <div style="margin: 15px 0; line-height: 1.8;"> ${domainsList} </div> <div style="background: #e8f5e8; padding: 15px; margin: 15px 0; border-radius: 8px;"> <h4 style="color: #2e7d32; margin-bottom: 10px;">๐Ÿ“‹ Domain Matching Rules:</h4> <ul style="margin: 0; padding-left: 20px;"> <li><strong>Exact match:</strong> "localhost" only matches "localhost"</li> <li><strong>Subdomain support:</strong> "example.com" allows "dev.example.com", "staging.example.com", etc.</li> <li><strong>Protocol flexible:</strong> Both HTTP and HTTPS are supported</li> </ul> </div> <div style="background: #f3e5f5; padding: 15px; margin: 15px 0; border-radius: 8px;"> <h4 style="color: #7b1fa2; margin-bottom: 10px;">โš™๏ธ Adding New Domains:</h4> <p>To add new development domains, contact the plugin development team to include your domains in the allowed list.</p> <p><strong>Example:</strong></p> <div style="background: #2d3748; color: #e2e8f0; padding: 10px; border-radius: 4px; font-family: monospace; margin: 10px 0;"> 'dev.mycompany.com',<br> 'staging.mycompany.com',<br> 'test.mycompany.com' </div> </div> `; resolve(content); }, () => { // Fallback if extension not available resolve(` <div style="background: #ffebee; border-left: 4px solid #f44336; padding: 15px; margin: 15px 0; border-radius: 4px;"> <h4 style="color: #c62828; margin-bottom: 10px;">โŒ Extension Not Available</h4> <p>Could not fetch allowed domains from the extension. Please ensure the FreshRoute extension is installed and enabled.</p> </div> <div style="margin: 15px 0;"> <button class="button secondary" onclick="ExtensionManager.checkStatus()">๐Ÿ”„ Check Extension</button> <button class="button secondary" onclick="GuideManager.show('allowed-domains')">๐Ÿ”„ Retry</button> </div> `); }); }); } }, 'troubleshooting': { title: '๐Ÿ”ง Troubleshooting Guide', content: ` <h3>โŒ Extension Not Working</h3> <p><strong>Solutions:</strong></p> <ul> <li>Check if extension is enabled in <code>chrome://extensions/</code></li> <li>Refresh the page after enabling extension</li> <li>Check browser console for errors (F12)</li> </ul> ` } }, show: function(guideKey) { const guide = this.guides[guideKey]; if (guide) { if (typeof guide.content === 'function') { // Check if it's an async function (returns a Promise) const result = guide.content(); if (result && typeof result.then === 'function') { // Handle async content this.showModal(`<h2>${guide.title}</h2><div style="padding: 20px; text-align: center;">๐Ÿ”„ Loading content...</div><div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`); result.then(content => { this.showModal(`<h2>${guide.title}</h2>${content}<div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`); }).catch(error => { console.error('Error loading guide content:', error); this.showModal(`<h2>${guide.title}</h2><div style="background: #ffebee; padding: 15px; border-radius: 4px;">โŒ Error loading content. Please try again.</div><div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`); }); } else { // Sync function this.showModal(`<h2>${guide.title}</h2>${result}<div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`); } } else { // Static content this.showModal(`<h2>${guide.title}</h2>${guide.content}<div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`); } } }, showModal: function(content) { document.getElementById('modalContent').innerHTML = content; document.getElementById('helpModal').style.display = 'block'; }, closeModal: function() { document.getElementById('helpModal').style.display = 'none'; }, toggleSection: function(sectionId) { const section = document.getElementById(sectionId); if (section) { section.style.display = section.style.display === 'none' ? 'block' : 'none'; } }, startInstallationGuide: function() { StepManager.highlightInstallation(); document.getElementById('installationInstructions').style.display = 'block'; this.toggleSection('chromeExtensionsInstructions'); this.toggleSection('manualSteps'); }, startAutoDetection: function() { let attempts = 0; const maxAttempts = 20; const statusDiv = document.getElementById('extensionStatus') || document.getElementById('autoCheckStatus'); const checkInterval = setInterval(() => { attempts++; statusDiv.innerHTML = `๐Ÿ” <strong>Waiting for extension installation... (${attempts}/${maxAttempts})</strong><br><small>Installing the extension? We'll detect it automatically!</small>`; window.postMessage({ type: 'FRESHROUTE_STATUS_REQUEST', source: 'setup-page', timestamp: Date.now() }, '*'); if (attempts >= maxAttempts) { clearInterval(checkInterval); statusDiv.innerHTML = `โฐ <strong>Still waiting for extension...</strong><br><small>Take your time! Click "Check Again" when you're done installing.</small>`; } }, 6000); }, showNextSteps: function() { this.showModal(` <h2>๐ŸŽ‰ Awesome! Extension is Ready!</h2> <p>Your FreshRoute extension is installed and working perfectly. Here's what you can do now:</p> <div style="background: #e8f5e8; padding: 20px; border-radius: 8px; margin: 15px 0;"> <h3>๐Ÿš€ Quick Start Options:</h3> <ol style="margin: 10px 0; padding-left: 20px; line-height: 1.8;"> <li><strong>URL Rewriting:</strong> Works immediately! No server needed.</li> <li><strong>Basic Headers:</strong> Add 'x-' prefixed headers without server.</li> <li><strong>API Mocking:</strong> Start server for full mock capabilities.</li> </ol> </div> <div style="margin-top: 20px;"> <button class="button" onclick="GuideManager.closeModal()">๐ŸŽ‰ I'm Ready to Go!</button> </div> `); } }; // Step management const StepManager = { markInstallationComplete: function() { const installStep = document.querySelector('.step'); if (installStep) { installStep.style.opacity = '0.6'; installStep.style.background = '#f8f9fa'; const stepNumber = installStep.querySelector('.step-number'); if (stepNumber) { stepNumber.innerHTML = 'โœ“'; stepNumber.style.background = '#28a745'; } } this.showUsageSteps(); }, highlightInstallation: function() { const installStep = document.querySelector('.step'); if (installStep) { installStep.style.opacity = '1'; installStep.style.background = 'white'; installStep.style.border = '2px solid #ff9800'; Utils.scrollToElement('.step'); } this.hideUsageSteps(); }, hideUsageSteps: function() { const steps = document.querySelectorAll('.step'); steps.forEach((step, index) => { if (index > 0) { step.style.opacity = '0.4'; step.style.background = '#f8f9fa'; } }); }, showUsageSteps: function() { const steps = document.querySelectorAll('.step'); steps.forEach((step, index) => { if (index > 0) { step.style.opacity = '1'; step.style.background = 'white';