aboutsummaryrefslogtreecommitdiff
path: root/apps/browser-extension/entrypoints
diff options
context:
space:
mode:
authorMaheshtheDev <[email protected]>2025-09-08 05:19:10 +0000
committerMaheshtheDev <[email protected]>2025-09-08 05:19:10 +0000
commit3e5ea2fd9ed210644ae29b013b579703e30986de (patch)
tree7de64cf7bddca72cf56e89ee4320005dad9f2ac5 /apps/browser-extension/entrypoints
parentfix: billing page (#416) (diff)
downloadsupermemory-3e5ea2fd9ed210644ae29b013b579703e30986de.tar.xz
supermemory-3e5ea2fd9ed210644ae29b013b579703e30986de.zip
extension: updated telemetry and batch upload (#415)
Diffstat (limited to 'apps/browser-extension/entrypoints')
-rw-r--r--apps/browser-extension/entrypoints/background.ts130
-rw-r--r--apps/browser-extension/entrypoints/content.ts565
-rw-r--r--apps/browser-extension/entrypoints/popup/App.tsx194
-rw-r--r--apps/browser-extension/entrypoints/popup/style.css1
-rw-r--r--apps/browser-extension/entrypoints/welcome/Welcome.tsx6
5 files changed, 456 insertions, 440 deletions
diff --git a/apps/browser-extension/entrypoints/background.ts b/apps/browser-extension/entrypoints/background.ts
index 1f4ae24e..05fef55d 100644
--- a/apps/browser-extension/entrypoints/background.ts
+++ b/apps/browser-extension/entrypoints/background.ts
@@ -1,47 +1,52 @@
-import { getDefaultProject, saveMemory, searchMemories } from "../utils/api";
+import { getDefaultProject, saveMemory, searchMemories } from "../utils/api"
import {
CONTAINER_TAGS,
CONTEXT_MENU_IDS,
MESSAGE_TYPES,
-} from "../utils/constants";
-import { captureTwitterTokens } from "../utils/twitter-auth";
+ POSTHOG_EVENT_KEY,
+} from "../utils/constants"
+import { trackEvent } from "../utils/posthog"
+import { captureTwitterTokens } from "../utils/twitter-auth"
import {
type TwitterImportConfig,
TwitterImporter,
-} from "../utils/twitter-import";
+} from "../utils/twitter-import"
import type {
ExtensionMessage,
MemoryData,
MemoryPayload,
-} from "../utils/types";
+} from "../utils/types"
export default defineBackground(() => {
- let twitterImporter: TwitterImporter | null = null;
+ let twitterImporter: TwitterImporter | null = null
- browser.runtime.onInstalled.addListener((details) => {
+ browser.runtime.onInstalled.addListener(async (details) => {
browser.contextMenus.create({
id: CONTEXT_MENU_IDS.SAVE_TO_SUPERMEMORY,
title: "Save to supermemory",
contexts: ["selection", "page", "link"],
- });
+ })
- // Open welcome tab on first install
if (details.reason === "install") {
+ await trackEvent("extension_installed", {
+ reason: details.reason,
+ version: browser.runtime.getManifest().version,
+ })
browser.tabs.create({
url: browser.runtime.getURL("/welcome.html"),
- });
+ })
}
- });
+ })
// Intercept Twitter requests to capture authentication headers.
browser.webRequest.onBeforeSendHeaders.addListener(
(details) => {
- captureTwitterTokens(details);
- return {};
+ captureTwitterTokens(details)
+ return {}
},
{ urls: ["*://x.com/*", "*://twitter.com/*"] },
["requestHeaders", "extraHeaders"],
- );
+ )
// Handle context menu clicks.
browser.contextMenus.onClicked.addListener(async (info, tab) => {
@@ -50,27 +55,28 @@ export default defineBackground(() => {
try {
await browser.tabs.sendMessage(tab.id, {
action: MESSAGE_TYPES.SAVE_MEMORY,
- });
+ actionSource: "context_menu",
+ })
} catch (error) {
- console.error("Failed to send message to content script:", error);
+ console.error("Failed to send message to content script:", error)
}
}
}
- });
+ })
// Send message to current active tab.
const sendMessageToCurrentTab = async (message: string) => {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
- });
+ })
if (tabs.length > 0 && tabs[0].id) {
await browser.tabs.sendMessage(tabs[0].id, {
type: MESSAGE_TYPES.IMPORT_UPDATE,
importedMessage: message,
- });
+ })
}
- };
+ }
/**
* Send import completion message
@@ -79,61 +85,71 @@ export default defineBackground(() => {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
- });
+ })
if (tabs.length > 0 && tabs[0].id) {
await browser.tabs.sendMessage(tabs[0].id, {
type: MESSAGE_TYPES.IMPORT_DONE,
totalImported,
- });
+ })
}
- };
+ }
/**
* Save memory to supermemory API
*/
const saveMemoryToSupermemory = async (
data: MemoryData,
+ actionSource: string,
): Promise<{ success: boolean; data?: unknown; error?: string }> => {
try {
- let containerTag: string = CONTAINER_TAGS.DEFAULT_PROJECT;
+ let containerTag: string = CONTAINER_TAGS.DEFAULT_PROJECT
try {
- const defaultProject = await getDefaultProject();
+ const defaultProject = await getDefaultProject()
if (defaultProject?.containerTag) {
- containerTag = defaultProject.containerTag;
+ containerTag = defaultProject.containerTag
}
} catch (error) {
- console.warn("Failed to get default project, using fallback:", error);
+ console.warn("Failed to get default project, using fallback:", error)
}
const payload: MemoryPayload = {
containerTags: [containerTag],
content: `${data.highlightedText}\n\n${data.html}\n\n${data?.url}`,
metadata: { sm_source: "consumer" },
- };
+ }
+
+ const responseData = await saveMemory(payload)
+
+ await trackEvent(POSTHOG_EVENT_KEY.SAVE_MEMORY_ATTEMPTED, {
+ source: `${POSTHOG_EVENT_KEY.SOURCE}_${actionSource}`,
+ has_highlight: !!data.highlightedText,
+ url_domain: data.url ? new URL(data.url).hostname : undefined,
+ })
- const responseData = await saveMemory(payload);
- return { success: true, data: responseData };
+ return { success: true, data: responseData }
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
- };
+ }
}
- };
+ }
const getRelatedMemories = async (
data: string,
+ eventSource: string,
): Promise<{ success: boolean; data?: unknown; error?: string }> => {
try {
const responseData = await searchMemories(data)
const response = responseData as {
results?: Array<{ memory?: string }>
}
- let memories = "";
+ let memories = ""
response.results?.forEach((result, index) => {
memories += `[${index + 1}] ${result.memory} `
- })
+ })
console.log("Memories:", memories)
+ await trackEvent(eventSource)
return { success: true, data: memories }
} catch (error) {
return {
@@ -141,7 +157,7 @@ export default defineBackground(() => {
error: error instanceof Error ? error.message : "Unknown error",
}
}
- };
+ }
/**
* Handle extension messages
@@ -154,48 +170,52 @@ export default defineBackground(() => {
onProgress: sendMessageToCurrentTab,
onComplete: sendImportDoneMessage,
onError: async (error: Error) => {
- await sendMessageToCurrentTab(`Error: ${error.message}`);
+ await sendMessageToCurrentTab(`Error: ${error.message}`)
},
- };
+ }
- twitterImporter = new TwitterImporter(importConfig);
- twitterImporter.startImport().catch(console.error);
- sendResponse({ success: true });
- return true;
+ twitterImporter = new TwitterImporter(importConfig)
+ twitterImporter.startImport().catch(console.error)
+ sendResponse({ success: true })
+ return true
}
// Handle regular memory save request
if (message.action === MESSAGE_TYPES.SAVE_MEMORY) {
- (async () => {
+ ;(async () => {
try {
const result = await saveMemoryToSupermemory(
message.data as MemoryData,
- );
- sendResponse(result);
+ message.actionSource || "unknown",
+ )
+ sendResponse(result)
} catch (error) {
sendResponse({
success: false,
error: error instanceof Error ? error.message : "Unknown error",
- });
+ })
}
- })();
- return true;
+ })()
+ return true
}
if (message.action === MESSAGE_TYPES.GET_RELATED_MEMORIES) {
- (async () => {
+ ;(async () => {
try {
- const result = await getRelatedMemories(message.data as string);
- sendResponse(result);
+ const result = await getRelatedMemories(
+ message.data as string,
+ message.actionSource || "unknown",
+ )
+ sendResponse(result)
} catch (error) {
sendResponse({
success: false,
error: error instanceof Error ? error.message : "Unknown error",
- });
+ })
}
- })();
- return true;
+ })()
+ return true
}
},
- );
-});
+ )
+})
diff --git a/apps/browser-extension/entrypoints/content.ts b/apps/browser-extension/entrypoints/content.ts
index 6c8a96c5..e755efa6 100644
--- a/apps/browser-extension/entrypoints/content.ts
+++ b/apps/browser-extension/entrypoints/content.ts
@@ -2,94 +2,105 @@ import {
DOMAINS,
ELEMENT_IDS,
MESSAGE_TYPES,
+ POSTHOG_EVENT_KEY,
STORAGE_KEYS,
-} from "../utils/constants";
+} from "../utils/constants"
+import { trackEvent } from "../utils/posthog"
import {
createChatGPTInputBarElement,
createClaudeInputBarElement,
- createSaveTweetElement,
createT3InputBarElement,
createTwitterImportButton,
- createTwitterImportUI,
DOMUtils,
-} from "../utils/ui-components";
+} from "../utils/ui-components"
export default defineContentScript({
matches: ["<all_urls>"],
main() {
- let twitterImportUI: HTMLElement | null = null;
- let isTwitterImportOpen = false;
browser.runtime.onMessage.addListener(async (message) => {
if (message.action === MESSAGE_TYPES.SHOW_TOAST) {
- DOMUtils.showToast(message.state);
+ DOMUtils.showToast(message.state)
} else if (message.action === MESSAGE_TYPES.SAVE_MEMORY) {
- await saveMemory();
+ await saveMemory()
} else if (message.type === MESSAGE_TYPES.IMPORT_UPDATE) {
- updateTwitterImportUI(message);
+ updateTwitterImportUI(message)
} else if (message.type === MESSAGE_TYPES.IMPORT_DONE) {
- updateTwitterImportUI(message);
+ updateTwitterImportUI(message)
}
- });
+ })
const observeForMemoriesDialog = () => {
const observer = new MutationObserver(() => {
if (DOMUtils.isOnDomain(DOMAINS.CHATGPT)) {
- addSupermemoryButtonToMemoriesDialog();
- addSaveChatGPTElementBeforeComposerBtn();
+ addSupermemoryButtonToMemoriesDialog()
+ addSaveChatGPTElementBeforeComposerBtn()
}
if (DOMUtils.isOnDomain(DOMAINS.CLAUDE)) {
- addSupermemoryIconToClaudeInput();
+ addSupermemoryIconToClaudeInput()
}
if (DOMUtils.isOnDomain(DOMAINS.T3)) {
- addSupermemoryIconToT3Input();
+ addSupermemoryIconToT3Input()
}
- if (DOMUtils.isOnDomain(DOMAINS.TWITTER)) {
- addTwitterImportButton();
- //addSaveTweetElement();
+ if (
+ DOMUtils.isOnDomain(DOMAINS.TWITTER) &&
+ window.location.pathname === "/i/bookmarks"
+ ) {
+ addTwitterImportButton()
+ } else if (DOMUtils.isOnDomain(DOMAINS.TWITTER)) {
+ if (DOMUtils.elementExists(ELEMENT_IDS.TWITTER_IMPORT_BUTTON)) {
+ DOMUtils.removeElement(ELEMENT_IDS.TWITTER_IMPORT_BUTTON)
+ }
}
- });
+ })
observer.observe(document.body, {
childList: true,
subtree: true,
- });
- };
+ })
+ }
- if (DOMUtils.isOnDomain(DOMAINS.TWITTER)) {
+ if (
+ DOMUtils.isOnDomain(DOMAINS.TWITTER) &&
+ window.location.pathname === "/i/bookmarks"
+ ) {
setTimeout(() => {
- addTwitterImportButton(); // Wait 2 seconds for page to load
+ addTwitterImportButton() // Wait 2 seconds for page to load
//addSaveTweetElement();
- }, 2000);
+ }, 2000)
+ } else if (DOMUtils.isOnDomain(DOMAINS.TWITTER)) {
+ if (DOMUtils.elementExists(ELEMENT_IDS.TWITTER_IMPORT_BUTTON)) {
+ DOMUtils.removeElement(ELEMENT_IDS.TWITTER_IMPORT_BUTTON)
+ }
}
if (DOMUtils.isOnDomain(DOMAINS.CLAUDE)) {
setTimeout(() => {
- addSupermemoryIconToClaudeInput(); // Wait 2 seconds for Claude page to load
- }, 2000);
+ addSupermemoryIconToClaudeInput() // Wait 2 seconds for Claude page to load
+ }, 2000)
}
if (DOMUtils.isOnDomain(DOMAINS.T3)) {
setTimeout(() => {
- addSupermemoryIconToT3Input(); // Wait 2 seconds for T3 page to load
- }, 2000);
+ addSupermemoryIconToT3Input() // Wait 2 seconds for T3 page to load
+ }, 2000)
}
if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", observeForMemoriesDialog);
+ document.addEventListener("DOMContentLoaded", observeForMemoriesDialog)
} else {
- observeForMemoriesDialog();
+ observeForMemoriesDialog()
}
async function saveMemory() {
try {
- DOMUtils.showToast("loading");
+ DOMUtils.showToast("loading")
- const highlightedText = window.getSelection()?.toString() || "";
+ const highlightedText = window.getSelection()?.toString() || ""
- const url = window.location.href;
+ const url = window.location.href
- const html = document.documentElement.outerHTML;
+ const html = document.documentElement.outerHTML
const response = await browser.runtime.sendMessage({
action: MESSAGE_TYPES.SAVE_MEMORY,
@@ -98,75 +109,77 @@ export default defineContentScript({
highlightedText,
url,
},
- });
+ actionSource: "context_menu",
+ })
- console.log("Response from enxtension:", response);
+ console.log("Response from enxtension:", response)
if (response.success) {
- DOMUtils.showToast("success");
+ DOMUtils.showToast("success")
} else {
- DOMUtils.showToast("error");
+ DOMUtils.showToast("error")
}
} catch (error) {
- console.error("Error saving memory:", error);
- DOMUtils.showToast("error");
+ console.error("Error saving memory:", error)
+ DOMUtils.showToast("error")
}
}
- async function getRelatedMemories() {
+ async function getRelatedMemories(actionSource: string) {
try {
const userQuery =
- document.getElementById("prompt-textarea")?.textContent || "";
+ document.getElementById("prompt-textarea")?.textContent || ""
const response = await browser.runtime.sendMessage({
action: MESSAGE_TYPES.GET_RELATED_MEMORIES,
data: userQuery,
- });
+ actionSource: actionSource,
+ })
if (response.success && response.data) {
- const promptElement = document.getElementById("prompt-textarea");
+ const promptElement = document.getElementById("prompt-textarea")
if (promptElement) {
- const currentContent = promptElement.innerHTML;
- promptElement.innerHTML = `${currentContent}<br>Supermemories: ${response.data}`;
+ const currentContent = promptElement.innerHTML
+ promptElement.innerHTML = `${currentContent}<br>Supermemories: ${response.data}`
}
}
} catch (error) {
- console.error("Error getting related memories:", error);
+ console.error("Error getting related memories:", error)
}
}
function addSupermemoryButtonToMemoriesDialog() {
- const dialogs = document.querySelectorAll('[role="dialog"]');
- let memoriesDialog: HTMLElement | null = null;
+ const dialogs = document.querySelectorAll('[role="dialog"]')
+ let memoriesDialog: HTMLElement | null = null
for (const dialog of dialogs) {
- const headerText = dialog.querySelector("h2");
+ const headerText = dialog.querySelector("h2")
if (headerText?.textContent?.includes("Saved memories")) {
- memoriesDialog = dialog as HTMLElement;
- break;
+ memoriesDialog = dialog as HTMLElement
+ break
}
}
- if (!memoriesDialog) return;
+ if (!memoriesDialog) return
- if (memoriesDialog.querySelector("#supermemory-save-button")) return;
+ if (memoriesDialog.querySelector("#supermemory-save-button")) return
const deleteAllContainer = memoriesDialog.querySelector(
".mt-5.flex.justify-end",
- );
- if (!deleteAllContainer) return;
+ )
+ if (!deleteAllContainer) return
- const supermemoryButton = document.createElement("button");
- supermemoryButton.id = "supermemory-save-button";
- supermemoryButton.className = "btn relative btn-primary-outline mr-2";
+ 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");
+ 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;
@@ -178,388 +191,352 @@ export default defineContentScript({
font-size: 14px !important;
margin-right: 8px !important;
cursor: pointer !important;
- `;
+ `
supermemoryButton.addEventListener("mouseenter", () => {
- supermemoryButton.style.backgroundColor = "#2B2E33";
- });
+ supermemoryButton.style.backgroundColor = "#2B2E33"
+ })
supermemoryButton.addEventListener("mouseleave", () => {
- supermemoryButton.style.backgroundColor = "#1C2026";
- });
+ supermemoryButton.style.backgroundColor = "#1C2026"
+ })
supermemoryButton.addEventListener("click", async () => {
- await saveMemoriesToSupermemory();
- });
+ await saveMemoriesToSupermemory()
+ })
deleteAllContainer.insertBefore(
supermemoryButton,
deleteAllContainer.firstChild,
- );
+ )
}
async function saveMemoriesToSupermemory() {
try {
- DOMUtils.showToast("loading");
+ DOMUtils.showToast("loading")
const memoriesTable = document.querySelector(
'[role="dialog"] table tbody',
- );
+ )
if (!memoriesTable) {
- DOMUtils.showToast("error");
- return;
+ DOMUtils.showToast("error")
+ return
}
- const memoryRows = memoriesTable.querySelectorAll("tr");
- const memories: string[] = [];
+ const memoryRows = memoriesTable.querySelectorAll("tr")
+ const memories: string[] = []
memoryRows.forEach((row) => {
- const memoryCell = row.querySelector("td .py-2.whitespace-pre-wrap");
+ const memoryCell = row.querySelector("td .py-2.whitespace-pre-wrap")
if (memoryCell?.textContent) {
- memories.push(memoryCell.textContent.trim());
+ memories.push(memoryCell.textContent.trim())
}
- });
+ })
- console.log("Memories:", memories);
+ console.log("Memories:", memories)
if (memories.length === 0) {
- DOMUtils.showToast("error");
- return;
+ DOMUtils.showToast("error")
+ return
}
- const combinedContent = `ChatGPT Saved Memories:\n\n${memories.map((memory, index) => `${index + 1}. ${memory}`).join("\n\n")}`;
+ const combinedContent = `ChatGPT Saved Memories:\n\n${memories.map((memory, index) => `${index + 1}. ${memory}`).join("\n\n")}`
const response = await browser.runtime.sendMessage({
action: MESSAGE_TYPES.SAVE_MEMORY,
data: {
html: combinedContent,
},
- });
+ actionSource: "chatgpt_memories_dialog",
+ })
- console.log({ response });
+ console.log({ response })
if (response.success) {
- DOMUtils.showToast("success");
+ DOMUtils.showToast("success")
} else {
- DOMUtils.showToast("error");
+ DOMUtils.showToast("error")
}
} catch (error) {
- console.error("Error saving memories to supermemory:", error);
- DOMUtils.showToast("error");
+ console.error("Error saving memories to supermemory:", error)
+ DOMUtils.showToast("error")
}
}
function addTwitterImportButton() {
if (!DOMUtils.isOnDomain(DOMAINS.TWITTER)) {
- return;
+ return
}
// Only show the import button on the bookmarks page
if (window.location.pathname !== "/i/bookmarks") {
- return;
+ return
}
if (DOMUtils.elementExists(ELEMENT_IDS.TWITTER_IMPORT_BUTTON)) {
- return;
- }
-
- const button = createTwitterImportButton(() => {
- showTwitterImportUI();
- });
-
- document.body.appendChild(button);
- }
-
- function showTwitterImportUI() {
- if (twitterImportUI) {
- twitterImportUI.remove();
+ return
}
- isTwitterImportOpen = true;
-
- // Check if user is authenticated
- browser.storage.local.get([STORAGE_KEYS.BEARER_TOKEN], (result) => {
- const isAuthenticated = !!result[STORAGE_KEYS.BEARER_TOKEN];
-
- twitterImportUI = createTwitterImportUI(
- hideTwitterImportUI,
- async () => {
- try {
- await browser.runtime.sendMessage({
- type: MESSAGE_TYPES.BATCH_IMPORT_ALL,
- });
- } catch (error) {
- console.error("Error starting import:", error);
- }
- },
- isAuthenticated,
- );
+ const button = createTwitterImportButton(async () => {
+ try {
+ await browser.runtime.sendMessage({
+ type: MESSAGE_TYPES.BATCH_IMPORT_ALL,
+ })
+ await trackEvent(POSTHOG_EVENT_KEY.TWITTER_IMPORT_STARTED, {
+ source: `${POSTHOG_EVENT_KEY.SOURCE}_content_script`,
+ })
+ } catch (error) {
+ console.error("Error starting import:", error)
+ }
+ })
- document.body.appendChild(twitterImportUI);
- });
+ document.body.appendChild(button)
}
- function hideTwitterImportUI() {
- if (twitterImportUI) {
- twitterImportUI.remove();
- twitterImportUI = null;
- }
- isTwitterImportOpen = false;
- }
function updateTwitterImportUI(message: {
- type: string;
- importedMessage?: string;
- totalImported?: number;
+ type: string
+ importedMessage?: string
+ totalImported?: number
}) {
- if (!isTwitterImportOpen || !twitterImportUI) return;
+ const importButton = document.getElementById(ELEMENT_IDS.TWITTER_IMPORT_BUTTON)
+ if (!importButton) return
- const statusDiv = twitterImportUI.querySelector(
- `#${ELEMENT_IDS.TWITTER_IMPORT_STATUS}`,
- );
- const button = twitterImportUI.querySelector(
- `#${ELEMENT_IDS.TWITTER_IMPORT_BTN}`,
- );
+ const iconUrl = browser.runtime.getURL("/icon-16.png")
if (message.type === MESSAGE_TYPES.IMPORT_UPDATE) {
- if (statusDiv) {
- statusDiv.innerHTML = `
- <div style="display: flex; align-items: center; gap: 8px; color: #92400e; background: #fef3c7; border: 1px solid #f59e0b; border-radius: 8px; padding: 8px 12px; font-size: 13px;">
- <div style="width: 12px; height: 12px; border: 2px solid #f59e0b; border-top: 2px solid transparent; border-radius: 50%; animation: spin 1s linear infinite;"></div>
- <span>${message.importedMessage}</span>
- </div>
- `;
- }
- if (button) {
- (button as HTMLButtonElement).disabled = true;
- (button as HTMLButtonElement).textContent = "Importing...";
- }
+ importButton.innerHTML = `
+ <img src="${iconUrl}" width="20" height="20" alt="Save to Memory" style="border-radius: 4px;" />
+ <span style="font-weight: 500; font-size: 14px;">${message.importedMessage}</span>
+ `
+ importButton.style.cursor = "default"
}
if (message.type === MESSAGE_TYPES.IMPORT_DONE) {
- if (statusDiv) {
- statusDiv.innerHTML = `
- <div style="display: flex; align-items: center; gap: 8px; color: #0369a1; background: #f0f9ff; border: 1px solid #0ea5e9; border-radius: 8px; padding: 8px 12px; font-size: 13px;">
- <span style="color: #059669;">✓</span>
- <span>Successfully imported ${message.totalImported} tweets!</span>
- </div>
- `;
- }
-
+ importButton.innerHTML = `
+ <img src="${iconUrl}" width="20" height="20" alt="Save to Memory" style="border-radius: 4px;" />
+ <span style="font-weight: 500; font-size: 14px; color: #059669;">✓ Imported ${message.totalImported} tweets!</span>
+ `
+
setTimeout(() => {
- hideTwitterImportUI();
- }, 3000);
+ importButton.innerHTML = `
+ <img src="${iconUrl}" width="20" height="20" alt="Save to Memory" style="border-radius: 4px;" />
+ <span style="font-weight: 500; font-size: 14px;">Import Bookmarks</span>
+ `
+ importButton.style.cursor = "pointer"
+ }, 3000)
}
}
function addSaveChatGPTElementBeforeComposerBtn() {
if (!DOMUtils.isOnDomain(DOMAINS.CHATGPT)) {
- return;
+ return
}
- const composerButtons = document.querySelectorAll("button.composer-btn");
+ const composerButtons = document.querySelectorAll("button.composer-btn")
composerButtons.forEach((button) => {
if (button.hasAttribute("data-supermemory-icon-added-before")) {
- return;
+ return
}
- const parent = button.parentElement;
- if (!parent) return;
+ const parent = button.parentElement
+ if (!parent) return
- const parentSiblings = parent.parentElement?.children;
- if (!parentSiblings) return;
+ const parentSiblings = parent.parentElement?.children
+ if (!parentSiblings) return
- let hasSpeechButtonSibling = false;
+ let hasSpeechButtonSibling = false
for (const sibling of parentSiblings) {
if (
sibling.getAttribute("data-testid") ===
"composer-speech-button-container"
) {
- hasSpeechButtonSibling = true;
- break;
+ hasSpeechButtonSibling = true
+ break
}
}
- if (!hasSpeechButtonSibling) return;
+ if (!hasSpeechButtonSibling) return
- const grandParent = parent.parentElement;
- if (!grandParent) return;
+ const grandParent = parent.parentElement
+ if (!grandParent) return
const existingIcon = grandParent.querySelector(
`#${ELEMENT_IDS.CHATGPT_INPUT_BAR_ELEMENT}-before-composer`,
- );
+ )
if (existingIcon) {
- button.setAttribute("data-supermemory-icon-added-before", "true");
- return;
+ button.setAttribute("data-supermemory-icon-added-before", "true")
+ return
}
const saveChatGPTElement = createChatGPTInputBarElement(async () => {
- await getRelatedMemories();
- });
+ await getRelatedMemories(
+ POSTHOG_EVENT_KEY.CHATGPT_CHAT_MEMORIES_SEARCHED,
+ )
+ })
- saveChatGPTElement.id = `${ELEMENT_IDS.CHATGPT_INPUT_BAR_ELEMENT}-before-composer-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
+ saveChatGPTElement.id = `${ELEMENT_IDS.CHATGPT_INPUT_BAR_ELEMENT}-before-composer-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`
- button.setAttribute("data-supermemory-icon-added-before", "true");
+ button.setAttribute("data-supermemory-icon-added-before", "true")
- grandParent.insertBefore(saveChatGPTElement, parent);
- });
+ grandParent.insertBefore(saveChatGPTElement, parent)
+ })
}
function addSupermemoryIconToClaudeInput() {
if (!DOMUtils.isOnDomain(DOMAINS.CLAUDE)) {
- return;
+ return
}
const targetContainers = document.querySelectorAll(
".relative.flex-1.flex.items-center.gap-2.shrink.min-w-0",
- );
+ )
targetContainers.forEach((container) => {
if (container.hasAttribute("data-supermemory-icon-added")) {
- return;
+ return
}
const existingIcon = container.querySelector(
`#${ELEMENT_IDS.CLAUDE_INPUT_BAR_ELEMENT}`,
- );
+ )
if (existingIcon) {
- container.setAttribute("data-supermemory-icon-added", "true");
- return;
+ container.setAttribute("data-supermemory-icon-added", "true")
+ return
}
const supermemoryIcon = createClaudeInputBarElement(async () => {
- await getRelatedMemoriesForClaude();
- });
+ await getRelatedMemoriesForClaude()
+ })
- supermemoryIcon.id = `${ELEMENT_IDS.CLAUDE_INPUT_BAR_ELEMENT}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
+ supermemoryIcon.id = `${ELEMENT_IDS.CLAUDE_INPUT_BAR_ELEMENT}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`
- container.setAttribute("data-supermemory-icon-added", "true");
+ container.setAttribute("data-supermemory-icon-added", "true")
- container.insertBefore(supermemoryIcon, container.firstChild);
- });
+ container.insertBefore(supermemoryIcon, container.firstChild)
+ })
}
async function getRelatedMemoriesForClaude() {
try {
- let userQuery = "";
+ let userQuery = ""
const supermemoryContainer = document.querySelector(
'[data-supermemory-icon-added="true"]',
- );
+ )
if (supermemoryContainer?.parentElement?.previousElementSibling) {
const pTag =
supermemoryContainer.parentElement.previousElementSibling.querySelector(
"p",
- );
- userQuery = pTag?.innerText || pTag?.textContent || "";
+ )
+ userQuery = pTag?.innerText || pTag?.textContent || ""
}
if (!userQuery.trim()) {
const textareaElement = document.querySelector(
'div[contenteditable="true"]',
- ) as HTMLElement;
+ ) as HTMLElement
userQuery =
- textareaElement?.innerText || textareaElement?.textContent || "";
+ textareaElement?.innerText || textareaElement?.textContent || ""
}
if (!userQuery.trim()) {
const inputElements = document.querySelectorAll(
'div[contenteditable="true"], textarea, input[type="text"]',
- );
+ )
for (const element of inputElements) {
const text =
(element as HTMLElement).innerText ||
- (element as HTMLInputElement).value;
+ (element as HTMLInputElement).value
if (text?.trim()) {
- userQuery = text.trim();
- break;
+ userQuery = text.trim()
+ break
}
}
}
- console.log("Claude query extracted:", userQuery);
+ console.log("Claude query extracted:", userQuery)
if (!userQuery.trim()) {
- console.log("No query text found");
- DOMUtils.showToast("error");
- return;
+ console.log("No query text found")
+ DOMUtils.showToast("error")
+ return
}
const response = await browser.runtime.sendMessage({
action: MESSAGE_TYPES.GET_RELATED_MEMORIES,
data: userQuery,
- });
+ actionSource: POSTHOG_EVENT_KEY.CLAUDE_CHAT_MEMORIES_SEARCHED,
+ })
- console.log("Claude memories response:", response);
+ console.log("Claude memories response:", response)
if (response.success && response.data) {
const textareaElement = document.querySelector(
'div[contenteditable="true"]',
- ) as HTMLElement;
+ ) as HTMLElement
if (textareaElement) {
- const currentContent = textareaElement.innerHTML;
- textareaElement.innerHTML = `${currentContent}<br>Supermemories: ${response.data}`;
+ const currentContent = textareaElement.innerHTML
+ textareaElement.innerHTML = `${currentContent}<br>Supermemories: ${response.data}`
- textareaElement.dispatchEvent(
- new Event("input", { bubbles: true }),
- );
+ textareaElement.dispatchEvent(new Event("input", { bubbles: true }))
} else {
- console.log("Could not find Claude input area");
+ console.log("Could not find Claude input area")
}
} else {
console.log(
"Failed to get memories:",
response.error || "Unknown error",
- );
+ )
}
} catch (error) {
- console.error("Error getting related memories for Claude:", error);
+ console.error("Error getting related memories for Claude:", error)
}
}
function addSupermemoryIconToT3Input() {
if (!DOMUtils.isOnDomain(DOMAINS.T3)) {
- return;
+ return
}
const targetContainers = document.querySelectorAll(
".flex.min-w-0.items-center.gap-2.overflow-hidden",
- );
+ )
targetContainers.forEach((container) => {
if (container.hasAttribute("data-supermemory-icon-added")) {
- return;
+ return
}
const existingIcon = container.querySelector(
`#${ELEMENT_IDS.T3_INPUT_BAR_ELEMENT}`,
- );
+ )
if (existingIcon) {
- container.setAttribute("data-supermemory-icon-added", "true");
- return;
+ container.setAttribute("data-supermemory-icon-added", "true")
+ return
}
const supermemoryIcon = createT3InputBarElement(async () => {
- await getRelatedMemoriesForT3();
- });
+ await getRelatedMemoriesForT3()
+ })
- supermemoryIcon.id = `${ELEMENT_IDS.T3_INPUT_BAR_ELEMENT}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
+ supermemoryIcon.id = `${ELEMENT_IDS.T3_INPUT_BAR_ELEMENT}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`
- container.setAttribute("data-supermemory-icon-added", "true");
+ container.setAttribute("data-supermemory-icon-added", "true")
- container.insertBefore(supermemoryIcon, container.firstChild);
- });
+ container.insertBefore(supermemoryIcon, container.firstChild)
+ })
}
async function getRelatedMemoriesForT3() {
try {
- let userQuery = "";
+ let userQuery = ""
const supermemoryContainer = document.querySelector(
'[data-supermemory-icon-added="true"]',
- );
+ )
if (
supermemoryContainer?.parentElement?.parentElement
?.previousElementSibling
@@ -567,48 +544,49 @@ export default defineContentScript({
const textareaElement =
supermemoryContainer.parentElement.parentElement.previousElementSibling.querySelector(
"textarea",
- );
- userQuery = textareaElement?.value || "";
+ )
+ userQuery = textareaElement?.value || ""
}
if (!userQuery.trim()) {
const textareaElement = document.querySelector(
'div[contenteditable="true"]',
- ) as HTMLElement;
+ ) as HTMLElement
userQuery =
- textareaElement?.innerText || textareaElement?.textContent || "";
+ textareaElement?.innerText || textareaElement?.textContent || ""
}
if (!userQuery.trim()) {
- const textareas = document.querySelectorAll("textarea");
+ const textareas = document.querySelectorAll("textarea")
for (const textarea of textareas) {
- const text = (textarea as HTMLTextAreaElement).value;
+ const text = (textarea as HTMLTextAreaElement).value
if (text?.trim()) {
- userQuery = text.trim();
- break;
+ userQuery = text.trim()
+ break
}
}
}
- console.log("T3 query extracted:", userQuery);
+ console.log("T3 query extracted:", userQuery)
if (!userQuery.trim()) {
- console.log("No query text found");
- return;
+ console.log("No query text found")
+ return
}
const response = await browser.runtime.sendMessage({
action: MESSAGE_TYPES.GET_RELATED_MEMORIES,
data: userQuery,
- });
+ actionSource: POSTHOG_EVENT_KEY.T3_CHAT_MEMORIES_SEARCHED,
+ })
- console.log("T3 memories response:", response);
+ console.log("T3 memories response:", response)
if (response.success && response.data) {
- let textareaElement = null;
+ let textareaElement = null
const supermemoryContainer = document.querySelector(
'[data-supermemory-icon-added="true"]',
- );
+ )
if (
supermemoryContainer?.parentElement?.parentElement
?.previousElementSibling
@@ -616,75 +594,41 @@ export default defineContentScript({
textareaElement =
supermemoryContainer.parentElement.parentElement.previousElementSibling.querySelector(
"textarea",
- );
+ )
}
if (!textareaElement) {
textareaElement = document.querySelector(
'div[contenteditable="true"]',
- ) as HTMLElement;
+ ) as HTMLElement
}
if (textareaElement) {
if (textareaElement.tagName === "TEXTAREA") {
const currentContent = (textareaElement as HTMLTextAreaElement)
- .value;
- (textareaElement as HTMLTextAreaElement).value =
- `${currentContent}\n\nSupermemories: ${response.data}`;
+ .value
+ ;(textareaElement as HTMLTextAreaElement).value =
+ `${currentContent}\n\nSupermemories: ${response.data}`
} else {
- const currentContent = textareaElement.innerHTML;
- textareaElement.innerHTML = `${currentContent}<br>Supermemories: ${response.data}`;
+ const currentContent = textareaElement.innerHTML
+ textareaElement.innerHTML = `${currentContent}<br>Supermemories: ${response.data}`
}
- textareaElement.dispatchEvent(
- new Event("input", { bubbles: true }),
- );
+ textareaElement.dispatchEvent(new Event("input", { bubbles: true }))
} else {
- console.log("Could not find T3 input area");
+ console.log("Could not find T3 input area")
}
} else {
console.log(
"Failed to get memories:",
response.error || "Unknown error",
- );
+ )
}
} catch (error) {
- console.error("Error getting related memories for T3:", error);
+ console.error("Error getting related memories for T3:", error)
}
}
- // TODO: Add Tweet Capture Functionality
- function _addSaveTweetElement() {
- if (!DOMUtils.isOnDomain(DOMAINS.TWITTER)) {
- return;
- }
-
- const targetDivs = document.querySelectorAll(
- "div.css-175oi2r.r-18u37iz.r-1h0z5md.r-1wron08",
- );
-
- targetDivs.forEach((targetDiv) => {
- if (targetDiv.hasAttribute("data-supermemory-icon-added")) {
- return;
- }
-
- const previousElement = targetDiv.previousElementSibling;
- if (previousElement?.id?.startsWith(ELEMENT_IDS.SAVE_TWEET_ELEMENT)) {
- targetDiv.setAttribute("data-supermemory-icon-added", "true");
- return;
- }
-
- const saveTweetElement = createSaveTweetElement(async () => {
- await saveMemory();
- });
-
- saveTweetElement.id = `${ELEMENT_IDS.SAVE_TWEET_ELEMENT}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
-
- targetDiv.setAttribute("data-supermemory-icon-added", "true");
-
- targetDiv.parentNode?.insertBefore(saveTweetElement, targetDiv);
- });
- }
document.addEventListener("keydown", async (event) => {
if (
@@ -692,18 +636,18 @@ export default defineContentScript({
event.shiftKey &&
event.key === "m"
) {
- event.preventDefault();
- await saveMemory();
+ event.preventDefault()
+ await saveMemory()
}
- });
+ })
window.addEventListener("message", (event) => {
if (event.source !== window) {
- return;
+ return
}
- const bearerToken = event.data.token;
-
- if (bearerToken) {
+ const bearerToken = event.data.token
+ const userData = event.data.userData
+ if (bearerToken && userData) {
if (
!(
window.location.hostname === "localhost" ||
@@ -712,18 +656,19 @@ export default defineContentScript({
)
) {
console.log(
- "Bearer token is only allowed to be used on localhost or supermemory.ai",
- );
- return;
+ "Bearer token and user data is only allowed to be used on localhost or supermemory.ai",
+ )
+ return
}
chrome.storage.local.set(
{
[STORAGE_KEYS.BEARER_TOKEN]: bearerToken,
+ [STORAGE_KEYS.USER_DATA]: userData,
},
() => {},
- );
+ )
}
- });
+ })
},
-});
+})
diff --git a/apps/browser-extension/entrypoints/popup/App.tsx b/apps/browser-extension/entrypoints/popup/App.tsx
index 3f960b34..ddb498c6 100644
--- a/apps/browser-extension/entrypoints/popup/App.tsx
+++ b/apps/browser-extension/entrypoints/popup/App.tsx
@@ -1,118 +1,152 @@
-import { useQueryClient } from "@tanstack/react-query";
-import { useEffect, useState } from "react";
-import "./App.css";
-import { STORAGE_KEYS } from "../../utils/constants";
+import { useQueryClient } from "@tanstack/react-query"
+import { useEffect, useState } from "react"
+import "./App.css"
+import { MESSAGE_TYPES, STORAGE_KEYS } from "../../utils/constants"
import {
useDefaultProject,
useProjects,
useSetDefaultProject,
-} from "../../utils/query-hooks";
-import type { Project } from "../../utils/types";
+} from "../../utils/query-hooks"
+import type { Project } from "../../utils/types"
function App() {
- const [userSignedIn, setUserSignedIn] = useState<boolean>(false);
- const [loading, setLoading] = useState<boolean>(true);
- const [showProjectSelector, setShowProjectSelector] =
- useState<boolean>(false);
- const [currentUrl, setCurrentUrl] = useState<string>("");
- const [currentTitle, setCurrentTitle] = useState<string>("");
- const [saving, setSaving] = useState<boolean>(false);
- const [activeTab, setActiveTab] = useState<"save" | "imports">("save");
+ const [userSignedIn, setUserSignedIn] = useState<boolean>(false)
+ const [loading, setLoading] = useState<boolean>(true)
+ const [showProjectSelector, setShowProjectSelector] = useState<boolean>(false)
+ const [currentUrl, setCurrentUrl] = useState<string>("")
+ const [currentTitle, setCurrentTitle] = useState<string>("")
+ const [saving, setSaving] = useState<boolean>(false)
+ const [activeTab, setActiveTab] = useState<"save" | "imports">("save")
- const queryClient = useQueryClient();
+ const queryClient = useQueryClient()
const { data: projects = [], isLoading: loadingProjects } = useProjects({
enabled: userSignedIn,
- });
+ })
const { data: defaultProject } = useDefaultProject({
enabled: userSignedIn,
- });
- const setDefaultProjectMutation = useSetDefaultProject();
+ })
+ const setDefaultProjectMutation = useSetDefaultProject()
useEffect(() => {
const checkAuthStatus = async () => {
try {
const result = await chrome.storage.local.get([
STORAGE_KEYS.BEARER_TOKEN,
- ]);
- const isSignedIn = !!result[STORAGE_KEYS.BEARER_TOKEN];
- setUserSignedIn(isSignedIn);
+ ])
+ const isSignedIn = !!result[STORAGE_KEYS.BEARER_TOKEN]
+ setUserSignedIn(isSignedIn)
} catch (error) {
- console.error("Error checking auth status:", error);
- setUserSignedIn(false);
+ console.error("Error checking auth status:", error)
+ setUserSignedIn(false)
} finally {
- setLoading(false);
+ setLoading(false)
}
- };
+ }
const getCurrentTab = async () => {
try {
const tabs = await chrome.tabs.query({
active: true,
currentWindow: true,
- });
+ })
if (tabs.length > 0 && tabs[0].url && tabs[0].title) {
- setCurrentUrl(tabs[0].url);
- setCurrentTitle(tabs[0].title);
+ setCurrentUrl(tabs[0].url)
+ setCurrentTitle(tabs[0].title)
}
} catch (error) {
- console.error("Error getting current tab:", error);
+ console.error("Error getting current tab:", error)
}
- };
+ }
- checkAuthStatus();
- getCurrentTab();
- }, []);
+ checkAuthStatus()
+ getCurrentTab()
+ }, [])
const handleProjectSelect = (project: Project) => {
setDefaultProjectMutation.mutate(project, {
onSuccess: () => {
- setShowProjectSelector(false);
+ setShowProjectSelector(false)
},
onError: (error) => {
- console.error("Error setting default project:", error);
+ console.error("Error setting default project:", error)
},
- });
- };
+ })
+ }
const handleShowProjectSelector = () => {
- setShowProjectSelector(true);
- };
+ setShowProjectSelector(true)
+ }
useEffect(() => {
if (!defaultProject && projects.length > 0) {
- const firstProject = projects[0];
- setDefaultProjectMutation.mutate(firstProject);
+ const firstProject = projects[0]
+ setDefaultProjectMutation.mutate(firstProject)
}
- }, [defaultProject, projects, setDefaultProjectMutation]);
+ }, [defaultProject, projects, setDefaultProjectMutation])
const handleSaveCurrentPage = async () => {
- setSaving(true);
+ setSaving(true)
+
try {
const tabs = await chrome.tabs.query({
active: true,
currentWindow: true,
- });
+ })
if (tabs.length > 0 && tabs[0].id) {
- await chrome.tabs.sendMessage(tabs[0].id, {
- action: "saveMemory",
- });
+ const response = await chrome.tabs.sendMessage(tabs[0].id, {
+ action: MESSAGE_TYPES.SAVE_MEMORY,
+ actionSource: "popup",
+ })
+
+ if (response?.success) {
+ await chrome.tabs.sendMessage(tabs[0].id, {
+ action: MESSAGE_TYPES.SHOW_TOAST,
+ state: "success",
+ })
+ } else {
+ await chrome.tabs.sendMessage(tabs[0].id, {
+ action: MESSAGE_TYPES.SHOW_TOAST,
+ state: "error",
+ })
+ }
+
+ window.close()
}
} catch (error) {
- console.error("Failed to save current page:", error);
+ console.error("Failed to save current page:", error)
+
+ try {
+ const tabs = await chrome.tabs.query({
+ active: true,
+ currentWindow: true,
+ })
+ if (tabs.length > 0 && tabs[0].id) {
+ await chrome.tabs.sendMessage(tabs[0].id, {
+ action: MESSAGE_TYPES.SHOW_TOAST,
+ state: "error",
+ })
+ }
+ } catch (toastError) {
+ console.error("Failed to show error toast:", toastError)
+ }
+
+ window.close()
} finally {
- setSaving(false);
+ setSaving(false)
}
- };
+ }
const handleSignOut = async () => {
try {
- await chrome.storage.local.remove([STORAGE_KEYS.BEARER_TOKEN]);
- setUserSignedIn(false);
- queryClient.clear();
+ await chrome.storage.local.remove([STORAGE_KEYS.BEARER_TOKEN])
+ await chrome.storage.local.remove([STORAGE_KEYS.USER_DATA])
+ await chrome.storage.local.remove([STORAGE_KEYS.DEFAULT_PROJECT])
+ setUserSignedIn(false)
+ queryClient.clear()
} catch (error) {
- console.error("Error signing out:", error);
+ console.error("Error signing out:", error)
}
- };
+ }
if (loading) {
return (
@@ -131,7 +165,7 @@ function App() {
<div>Loading...</div>
</div>
</div>
- );
+ )
}
return (
@@ -267,11 +301,11 @@ function App() {
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<button
- className="w-full py-3 px-3 bg-white text-black border border-gray-200 rounded-md text-sm font-medium cursor-pointer flex items-center justify-center transition-colors duration-200 hover:bg-gray-50"
+ className="w-full py-3 px-3 bg-white text-black border border-gray-200 rounded-md text-sm font-medium cursor-pointer flex items-center justify-start transition-colors duration-200 hover:bg-gray-50"
onClick={() => {
chrome.tabs.create({
url: "https://chatgpt.com/#settings/Personalization",
- });
+ })
}}
type="button"
>
@@ -286,17 +320,33 @@ function App() {
<title>OpenAI</title>
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
</svg>
- Import ChatGPT Memories
+ <div className="text-left">
+ <p>Import ChatGPT Memories</p>
+ <p className="m-0 text-[10px] text-gray-500 leading-tight">
+ open 'manage', save your memories to supermemory
+ </p>
+ </div>
</button>
</div>
<div className="flex flex-col gap-2">
<button
className="w-full py-3 px-3 bg-white text-black border border-gray-200 rounded-md text-sm font-medium cursor-pointer flex items-center justify-center transition-colors duration-200 outline-none appearance-none hover:bg-gray-50 focus:outline-none"
- onClick={() => {
- chrome.tabs.create({
- url: "https://x.com/i/bookmarks",
- });
+ onClick={async () => {
+ const [activeTab] = await chrome.tabs.query({
+ active: true,
+ currentWindow: true,
+ })
+
+ const targetUrl = "https://x.com/i/bookmarks"
+
+ if (activeTab?.url === targetUrl) {
+ return
+ }
+
+ await chrome.tabs.create({
+ url: targetUrl,
+ })
}}
type="button"
>
@@ -310,11 +360,13 @@ function App() {
<title>X Twitter Logo</title>
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
</svg>
- Import X Bookmarks
+ <div className="text-left">
+ <p>Import X/Twitter Bookmarks</p>
+ <p className="m-0 text-[10px] text-gray-500 leading-tight">
+ Click on supermemory on top right to import bookmarks
+ </p>
+ </div>
</button>
- <p className="m-0 text-xs text-gray-500 leading-tight pl-1">
- Click on supermemory on top right to import bookmarks
- </p>
</div>
</div>
</div>
@@ -393,7 +445,7 @@ function App() {
<button
className="bg-transparent border-none text-blue-500 cursor-pointer underline text-sm p-0 hover:text-blue-700"
onClick={() => {
- window.open("mailto:[email protected]", "_blank");
+ window.open("mailto:[email protected]", "_blank")
}}
type="button"
>
@@ -408,7 +460,7 @@ function App() {
url: import.meta.env.PROD
? "https://app.supermemory.ai/login"
: "http://localhost:3000/login",
- });
+ })
}}
type="button"
>
@@ -419,7 +471,7 @@ function App() {
)}
</div>
</div>
- );
+ )
}
-export default App;
+export default App
diff --git a/apps/browser-extension/entrypoints/popup/style.css b/apps/browser-extension/entrypoints/popup/style.css
index 684e1ac6..ea67f153 100644
--- a/apps/browser-extension/entrypoints/popup/style.css
+++ b/apps/browser-extension/entrypoints/popup/style.css
@@ -15,7 +15,6 @@
-webkit-text-size-adjust: 100%;
}
-
@media (prefers-color-scheme: light) {
:root {
color: #213547;
diff --git a/apps/browser-extension/entrypoints/welcome/Welcome.tsx b/apps/browser-extension/entrypoints/welcome/Welcome.tsx
index 00bd01cc..9463eba4 100644
--- a/apps/browser-extension/entrypoints/welcome/Welcome.tsx
+++ b/apps/browser-extension/entrypoints/welcome/Welcome.tsx
@@ -77,7 +77,7 @@ function Welcome() {
url: import.meta.env.PROD
? "https://app.supermemory.ai/login"
: "http://localhost:3000/login",
- });
+ })
}}
type="button"
>
@@ -101,7 +101,7 @@ function Welcome() {
</div>
</div>
</div>
- );
+ )
}
-export default Welcome;
+export default Welcome