/**
* Context Bridge - ChatGPT Content Script
* Injects "Insert Context" button into ChatGPT interface
*/
console.log('Context Bridge: Loaded on ChatGPT');
let contextUrl = null;
let isInjected = false;
// Get context URL from storage
chrome.runtime.sendMessage({ action: 'getContextUrl' }, (response) => {
if (response && response.rawUrl) {
contextUrl = response.rawUrl;
injectButton();
}
});
// Listen for storage changes
chrome.storage.onChanged.addListener((changes, namespace) => {
if (namespace === 'sync' && changes.rawUrl) {
contextUrl = changes.rawUrl.newValue;
updateButton();
}
});
function injectButton() {
// Find the textarea (ChatGPT uses a textarea)
const textarea = document.querySelector('textarea[placeholder*="Message"]') ||
document.querySelector('textarea');
if (!textarea) {
setTimeout(injectButton, 500);
return;
}
// Check if button already exists
if (document.querySelector('.context-bridge-button')) {
return;
}
// Find the container with the send button
const formContainer = textarea.closest('form') || textarea.parentElement;
if (!formContainer) {
return;
}
// Create button
const button = document.createElement('button');
button.type = 'button';
button.className = 'context-bridge-button';
button.innerHTML = `
Insert Context
Click to insert your context
`;
// Style to position near send button
button.style.position = 'absolute';
button.style.right = '60px';
button.style.bottom = '12px';
// Add click handler with improvements
let isInserting = false;
let lastInsertTime = 0;
const COOLDOWN_MS = 1000;
button.addEventListener('click', async (e) => {
e.preventDefault();
e.stopPropagation();
// Rate limiting
const now = Date.now();
if (now - lastInsertTime < COOLDOWN_MS || isInserting) {
return;
}
if (!contextUrl) {
alert('No context URL set. Click the Context Bridge extension icon to configure.');
return;
}
// Loading state
isInserting = true;
button.disabled = true;
button.classList.add('loading');
button.innerHTML = `
Inserting...
`;
try {
// Verify context is accessible
const response = await fetch(contextUrl);
if (!response.ok) {
throw new Error(`Failed to fetch context (HTTP ${response.status})`);
}
// Insert context message
const message = `Read ${contextUrl} first, then help me with: `;
// Set textarea value
textarea.value = message;
textarea.focus();
// Trigger input event so ChatGPT recognizes the change
const inputEvent = new Event('input', { bubbles: true });
textarea.dispatchEvent(inputEvent);
// Position cursor at end
textarea.setSelectionRange(message.length, message.length);
// Success state
lastInsertTime = now;
button.classList.remove('loading');
button.classList.add('injected');
button.innerHTML = `
Context Inserted ✓
`;
// Reset after 2 seconds
setTimeout(() => {
button.classList.remove('injected');
button.disabled = false;
button.innerHTML = `
Insert Context
Click to insert your context
`;
isInserting = false;
}, 2000);
} catch (error) {
// Error state
console.error('Context Bridge error:', error);
button.classList.remove('loading');
button.classList.add('error');
button.innerHTML = `
Failed
`;
alert(`Failed to insert context:\n\n${error.message}\n\nPlease check:\n1. Context URL is accessible\n2. You're connected to the internet\n3. Try refreshing the page`);
setTimeout(() => {
button.classList.remove('error');
button.disabled = false;
button.innerHTML = `
Insert Context
Click to insert your context
`;
isInserting = false;
}, 3000);
}
});
// Add button to the form
formContainer.style.position = 'relative';
formContainer.appendChild(button);
console.log('Context Bridge: Button injected on ChatGPT');
}
function updateButton() {
const button = document.querySelector('.context-bridge-button');
if (!button) {
if (contextUrl) {
injectButton();
}
}
}
// Watch for DOM changes (ChatGPT is a SPA)
const observer = new MutationObserver((mutations) => {
if (!document.querySelector('.context-bridge-button') && contextUrl) {
injectButton();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// Initial injection
if (contextUrl) {
injectButton();
}