/** * Context Bridge - Popup Script */ let contextUrl = null; let rawUrl = null; // Load state chrome.storage.sync.get(['contextUrl', 'rawUrl'], (result) => { contextUrl = result.contextUrl; rawUrl = result.rawUrl; updateUI(); }); function updateUI() { const noContext = document.getElementById('no-context'); const hasContext = document.getElementById('has-context'); const setContext = document.getElementById('set-context'); if (rawUrl) { noContext.style.display = 'none'; hasContext.style.display = 'block'; setContext.style.display = 'none'; document.getElementById('context-url-display').value = rawUrl; } else { noContext.style.display = 'block'; hasContext.style.display = 'none'; setContext.style.display = 'none'; } } // Copy button document.getElementById('copy-btn')?.addEventListener('click', () => { const input = document.getElementById('context-url-display'); input.select(); document.execCommand('copy'); const btn = document.getElementById('copy-btn'); btn.innerHTML = ''; setTimeout(() => { btn.innerHTML = ` `; }, 1000); }); // Preview button document.getElementById('preview-btn')?.addEventListener('click', async () => { if (!rawUrl) return; const btn = document.getElementById('preview-btn'); btn.innerHTML = ' Loading...'; btn.disabled = true; try { const response = await fetch(rawUrl); const content = await response.text(); // Escape HTML to prevent XSS const escapeHtml = (text) => { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }; // Open in new tab with safely escaped content const previewWindow = window.open('', '_blank'); previewWindow.document.write(` Context Preview

Your Context Preview

Source: ${escapeHtml(rawUrl)}
${escapeHtml(content)}
`); previewWindow.document.close(); } catch (error) { alert('Failed to load context: ' + error.message); } finally { btn.innerHTML = ` Preview Context `; btn.disabled = false; } }); // Change button document.getElementById('change-btn')?.addEventListener('click', () => { document.getElementById('has-context').style.display = 'none'; document.getElementById('set-context').style.display = 'block'; document.getElementById('context-url-input').value = rawUrl || ''; }); // Save button document.getElementById('save-btn')?.addEventListener('click', async () => { const url = document.getElementById('context-url-input').value.trim(); const btn = document.getElementById('save-btn'); const originalText = btn.innerHTML; if (!url) { alert('Please enter a URL'); return; } // Better URL validation - check actual domain let urlObj; try { urlObj = new URL(url); } catch (e) { alert('Invalid URL format. Please enter a valid URL.'); return; } // Check if it's from GitHub domains const validDomains = ['gist.github.com', 'gist.githubusercontent.com', 'raw.githubusercontent.com']; const isValidDomain = validDomains.some(domain => urlObj.hostname === domain || urlObj.hostname.endsWith('.' + domain)); if (!isValidDomain) { alert('Please enter a valid GitHub Gist raw URL.\n\nAccepted domains:\n- gist.github.com\n- gist.githubusercontent.com\n- raw.githubusercontent.com'); return; } // Validate URL actually returns content btn.innerHTML = '⏳ Validating...'; btn.disabled = true; try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const content = await response.text(); if (content.length === 0) { throw new Error('URL returned empty content'); } // Check if it looks like HTML instead of markdown/text if (content.trim().startsWith(' { rawUrl = url; contextUrl = url; btn.innerHTML = '✓ Saved!'; setTimeout(() => { updateUI(); }, 1000); }); } catch (error) { alert(`Failed to validate URL:\n\n${error.message}\n\nPlease check:\n1. URL is accessible\n2. It's the raw gist URL\n3. You're connected to the internet`); btn.innerHTML = originalText; btn.disabled = false; } }); // Cancel button document.getElementById('cancel-btn')?.addEventListener('click', () => { updateUI(); }); // Clear button document.getElementById('clear-btn')?.addEventListener('click', (e) => { e.preventDefault(); if (confirm('Clear your context URL?')) { chrome.storage.sync.remove(['contextUrl', 'rawUrl'], () => { contextUrl = null; rawUrl = null; updateUI(); }); } });