added chatgpt import feature

This commit is contained in:
Mahesh Sanikommmu 2025-08-21 11:23:41 -07:00
parent c9f0dcf969
commit bd580f4e66
7 changed files with 189 additions and 9 deletions

View file

@ -47,11 +47,11 @@ export default defineBackground(() => {
body: JSON.stringify({
containerTags: ['sm_project_default'],
content:
message.data.highlightedText +
message.data?.highlightedText +
'\n\n' +
message.data.html +
'\n\n' +
message.data.url,
message.data?.url,
metadata: { sm_source: 'consumer' },
}),
});

View file

@ -11,6 +11,29 @@ export default defineContentScript({
}
});
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');
@ -42,6 +65,113 @@ export default defineContentScript({
}
}
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();

View file

@ -81,3 +81,36 @@
.authenticated, .unauthenticated {
text-align: left;
}
.actions {
display: flex;
flex-direction: column;
gap: 12px;
}
.chatgpt-btn {
width: 100%;
padding: 12px 12px;
background-color: white;
color: black;
border: 1px solid #e4e6eb;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s ease;
}
.chatgpt-btn:hover {
background-color: #f0f0f0;
border-color: #e4e6eb;
}
.chatgpt-logo {
width: 35px;
height: 20px;
flex-shrink: 0;
}

View file

@ -57,9 +57,26 @@ function App() {
<span className="status-indicator signed-in"></span>
<span>Signed in</span>
</div>
<button className="sign-out-btn" onClick={handleSignOut}>
Sign Out
</button>
<div className="actions">
<button
onClick={() => {
chrome.tabs.create({
url: 'https://chatgpt.com/#settings/Personalization',
});
}}
className="chatgpt-btn"
>
<img
src="https://upload.wikimedia.org/wikipedia/commons/1/13/ChatGPT-Logo.png"
alt="ChatGPT"
className="chatgpt-logo"
/>
Import ChatGPT Memories
</button>
<button className="sign-out-btn" onClick={handleSignOut}>
Sign Out
</button>
</div>
</div>
) : (
<div className="unauthenticated">

View file

@ -1,5 +1,5 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
font-family: 'Space Grotesk', Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;

View file

@ -1,6 +1,6 @@
{
"name": "Supermemory",
"description": "supermemory",
"name": "supermemory-browser-extension",
"description": "Supermemory Browser Extension",
"private": true,
"version": "0.0.1",
"type": "module",

View file

@ -1,5 +1,5 @@
{
"name": "supermemory-app",
"name": "supermemory",
"private": true,
"scripts": {
"build": "turbo run build",