mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-06 08:16:03 +00:00
342 lines
11 KiB
TypeScript
342 lines
11 KiB
TypeScript
export default defineContentScript({
|
|
matches: ['<all_urls>'],
|
|
main() {
|
|
let currentToast: HTMLElement | null = null;
|
|
|
|
browser.runtime.onMessage.addListener(async (message) => {
|
|
if (message.action === 'showToast') {
|
|
showToast(message.state);
|
|
} else if (message.action === 'saveMemory') {
|
|
await saveMemory();
|
|
}
|
|
});
|
|
|
|
const observeForMemoriesDialog = () => {
|
|
const observer = new MutationObserver(() => {
|
|
if (window.location.hostname === 'chatgpt.com' || window.location.hostname === 'chat.openai.com') {
|
|
addSupermemoryButtonToMemoriesDialog();
|
|
}
|
|
});
|
|
|
|
observer.observe(document.body, {
|
|
childList: true,
|
|
subtree: true,
|
|
});
|
|
|
|
if (window.location.hostname === 'chatgpt.com' || window.location.hostname === 'chat.openai.com') {
|
|
addSupermemoryButtonToMemoriesDialog();
|
|
}
|
|
};
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', observeForMemoriesDialog);
|
|
} else {
|
|
observeForMemoriesDialog();
|
|
}
|
|
|
|
async function saveMemory() {
|
|
try {
|
|
showToast('loading');
|
|
|
|
const highlightedText = window.getSelection()?.toString() || '';
|
|
|
|
const url = window.location.href;
|
|
|
|
const html = document.documentElement.outerHTML;
|
|
|
|
const response = await browser.runtime.sendMessage({
|
|
action: 'saveMemory',
|
|
data: {
|
|
html,
|
|
highlightedText,
|
|
url,
|
|
},
|
|
});
|
|
|
|
console.log('Response from enxtension:', response);
|
|
if (response.success) {
|
|
showToast('success');
|
|
} else {
|
|
showToast('error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving memory:', error);
|
|
showToast('error');
|
|
}
|
|
}
|
|
|
|
function addSupermemoryButtonToMemoriesDialog() {
|
|
const dialogs = document.querySelectorAll('[role="dialog"]');
|
|
let memoriesDialog: HTMLElement | null = null;
|
|
|
|
for (const dialog of dialogs) {
|
|
const headerText = dialog.querySelector('h2');
|
|
if (headerText && headerText.textContent?.includes('Saved memories')) {
|
|
memoriesDialog = dialog as HTMLElement;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!memoriesDialog) return;
|
|
|
|
if (memoriesDialog.querySelector('#supermemory-save-button')) return;
|
|
|
|
const deleteAllContainer = memoriesDialog.querySelector('.mt-5.flex.justify-end');
|
|
if (!deleteAllContainer) return;
|
|
|
|
const supermemoryButton = document.createElement('button');
|
|
supermemoryButton.id = 'supermemory-save-button';
|
|
supermemoryButton.className = 'btn relative btn-primary-outline mr-2';
|
|
|
|
const iconUrl = browser.runtime.getURL('/icon-16.png');
|
|
|
|
supermemoryButton.innerHTML = `
|
|
<div class="flex items-center justify-center gap-2">
|
|
<img src="${iconUrl}" alt="Supermemory" style="width: 16px; height: 16px; flex-shrink: 0; border-radius: 2px;" />
|
|
Save to Supermemory
|
|
</div>
|
|
`;
|
|
|
|
supermemoryButton.style.cssText = `
|
|
background: #1C2026 !important;
|
|
color: white !important;
|
|
border: 1px solid #1C2026 !important;
|
|
border-radius: 9999px !important;
|
|
padding: 10px 16px !important;
|
|
font-weight: 500 !important;
|
|
font-size: 14px !important;
|
|
margin-right: 8px !important;
|
|
cursor: pointer !important;
|
|
`;
|
|
|
|
supermemoryButton.addEventListener('mouseenter', () => {
|
|
supermemoryButton.style.backgroundColor = '#2B2E33';
|
|
});
|
|
|
|
supermemoryButton.addEventListener('mouseleave', () => {
|
|
supermemoryButton.style.backgroundColor = '#1C2026';
|
|
});
|
|
|
|
supermemoryButton.addEventListener('click', async () => {
|
|
await saveMemoriesToSupermemory();
|
|
});
|
|
|
|
deleteAllContainer.insertBefore(supermemoryButton, deleteAllContainer.firstChild);
|
|
}
|
|
|
|
async function saveMemoriesToSupermemory() {
|
|
try {
|
|
showToast('loading');
|
|
|
|
const memoriesTable = document.querySelector('[role="dialog"] table tbody');
|
|
if (!memoriesTable) {
|
|
showToast('error');
|
|
return;
|
|
}
|
|
|
|
const memoryRows = memoriesTable.querySelectorAll('tr');
|
|
const memories: string[] = [];
|
|
|
|
memoryRows.forEach(row => {
|
|
const memoryCell = row.querySelector('td .py-2.whitespace-pre-wrap');
|
|
if (memoryCell && memoryCell.textContent) {
|
|
memories.push(memoryCell.textContent.trim());
|
|
}
|
|
});
|
|
|
|
console.log('Memories:', memories);
|
|
|
|
if (memories.length === 0) {
|
|
showToast('error');
|
|
return;
|
|
}
|
|
|
|
const url = window.location.href;
|
|
const combinedContent = `ChatGPT Saved Memories:\n\n${memories.map((memory, index) => `${index + 1}. ${memory}`).join('\n\n')}`;
|
|
|
|
const response = await browser.runtime.sendMessage({
|
|
action: 'saveMemory',
|
|
data: {
|
|
html: combinedContent,
|
|
},
|
|
});
|
|
|
|
if (response.success) {
|
|
showToast('success');
|
|
} else {
|
|
showToast('error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving memories to Supermemory:', error);
|
|
showToast('error');
|
|
}
|
|
}
|
|
|
|
function showToast(state: 'loading' | 'success' | 'error') {
|
|
if (currentToast) {
|
|
currentToast.remove();
|
|
}
|
|
|
|
const toast = document.createElement('div');
|
|
toast.id = 'supermemory-toast';
|
|
|
|
toast.style.cssText = `
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
z-index: 2147483647;
|
|
background: #ffffff;
|
|
border-radius: 9999px;
|
|
padding: 12px 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
font-size: 14px;
|
|
color: #374151;
|
|
min-width: 200px;
|
|
max-width: 300px;
|
|
animation: slideIn 0.3s ease-out;
|
|
`;
|
|
|
|
if (!document.getElementById('supermemory-toast-styles')) {
|
|
const style = document.createElement('style');
|
|
style.id = 'supermemory-toast-styles';
|
|
style.textContent = `
|
|
@keyframes slideIn {
|
|
from {
|
|
transform: translateX(100%);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
@keyframes fadeOut {
|
|
from {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
to {
|
|
transform: translateX(100%);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
const icon = document.createElement('div');
|
|
icon.style.cssText = `
|
|
width: 20px;
|
|
height: 20px;
|
|
flex-shrink: 0;
|
|
`;
|
|
|
|
const text = document.createElement('span');
|
|
text.style.cssText = `
|
|
font-weight: 500;
|
|
`;
|
|
|
|
if (state === 'loading') {
|
|
icon.innerHTML = `
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M12 6V2" stroke="#6366f1" stroke-width="2" stroke-linecap="round"/>
|
|
<path d="M12 22V18" stroke="#6366f1" stroke-width="2" stroke-linecap="round" opacity="0.3"/>
|
|
<path d="M20.49 8.51L18.36 6.38" stroke="#6366f1" stroke-width="2" stroke-linecap="round" opacity="0.7"/>
|
|
<path d="M5.64 17.64L3.51 15.51" stroke="#6366f1" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
|
|
<path d="M22 12H18" stroke="#6366f1" stroke-width="2" stroke-linecap="round" opacity="0.8"/>
|
|
<path d="M6 12H2" stroke="#6366f1" stroke-width="2" stroke-linecap="round" opacity="0.4"/>
|
|
<path d="M20.49 15.49L18.36 17.62" stroke="#6366f1" stroke-width="2" stroke-linecap="round" opacity="0.9"/>
|
|
<path d="M5.64 6.36L3.51 8.49" stroke="#6366f1" stroke-width="2" stroke-linecap="round" opacity="0.6"/>
|
|
</svg>
|
|
`;
|
|
icon.style.animation = 'spin 1s linear infinite';
|
|
if (!document.getElementById('supermemory-spinner-styles')) {
|
|
const spinStyle = document.createElement('style');
|
|
spinStyle.id = 'supermemory-spinner-styles';
|
|
spinStyle.textContent = `
|
|
@keyframes spin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
`;
|
|
document.head.appendChild(spinStyle);
|
|
}
|
|
text.textContent = 'Adding to Memory...';
|
|
} else if (state === 'success') {
|
|
const iconUrl = browser.runtime.getURL('/icon-16.png');
|
|
icon.innerHTML = `
|
|
<img src="${iconUrl}" width="20" height="20" alt="Success" style="border-radius: 2px;" />
|
|
`;
|
|
text.textContent = 'Added to Memory';
|
|
} else if (state === 'error') {
|
|
icon.innerHTML = `
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<circle cx="12" cy="12" r="10" fill="#ef4444"/>
|
|
<path d="M15 9L9 15" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
<path d="M9 9L15 15" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
`;
|
|
text.textContent = 'Failed to save memory / Make sure you are logged in';
|
|
}
|
|
|
|
toast.appendChild(icon);
|
|
toast.appendChild(text);
|
|
document.body.appendChild(toast);
|
|
currentToast = toast;
|
|
|
|
if (state === 'success' || state === 'error') {
|
|
setTimeout(() => {
|
|
if (currentToast === toast) {
|
|
toast.style.animation = 'fadeOut 0.3s ease-out';
|
|
setTimeout(() => {
|
|
if (toast.parentNode) {
|
|
toast.remove();
|
|
}
|
|
if (currentToast === toast) {
|
|
currentToast = null;
|
|
}
|
|
}, 300);
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
document.addEventListener('keydown', async (event) => {
|
|
if (
|
|
(event.ctrlKey || event.metaKey) &&
|
|
event.shiftKey &&
|
|
event.key === 'm'
|
|
) {
|
|
event.preventDefault();
|
|
await saveMemory();
|
|
}
|
|
});
|
|
|
|
window.addEventListener('message', (event) => {
|
|
if (event.source !== window) {
|
|
return;
|
|
}
|
|
const bearerToken = event.data.token;
|
|
|
|
if (bearerToken) {
|
|
if (
|
|
!(
|
|
window.location.hostname === 'localhost' ||
|
|
window.location.hostname === 'supermemory.ai' ||
|
|
window.location.hostname === 'app.supermemory.ai'
|
|
)
|
|
) {
|
|
console.log(
|
|
'Bearer token is only allowed to be used on localhost or supermemory.ai'
|
|
);
|
|
return;
|
|
}
|
|
|
|
chrome.storage.local.set({ bearerToken }, () => {});
|
|
}
|
|
});
|
|
},
|
|
});
|