/** * 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(`
${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();
});
}
});