diff options
Diffstat (limited to 'apps/browser-extension/entrypoints')
| -rw-r--r-- | apps/browser-extension/entrypoints/background.ts | 156 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/content/chatgpt.ts | 497 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/content/claude.ts | 421 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/content/index.ts | 64 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/content/shared.ts | 46 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/content/t3.ts | 455 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/content/twitter.ts | 60 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/popup/App.tsx | 155 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/popup/main.tsx | 16 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/welcome/Welcome.tsx | 6 | ||||
| -rw-r--r-- | apps/browser-extension/entrypoints/welcome/main.tsx | 16 |
11 files changed, 958 insertions, 934 deletions
diff --git a/apps/browser-extension/entrypoints/background.ts b/apps/browser-extension/entrypoints/background.ts index 9d46a5e9..b71b54c7 100644 --- a/apps/browser-extension/entrypoints/background.ts +++ b/apps/browser-extension/entrypoints/background.ts @@ -1,52 +1,52 @@ -import { getDefaultProject, saveMemory, searchMemories } from "../utils/api" +import { getDefaultProject, saveMemory, searchMemories } from "../utils/api"; import { CONTAINER_TAGS, CONTEXT_MENU_IDS, MESSAGE_TYPES, POSTHOG_EVENT_KEY, -} from "../utils/constants" -import { trackEvent } from "../utils/posthog" -import { captureTwitterTokens } from "../utils/twitter-auth" +} 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(async (details) => { browser.contextMenus.create({ id: CONTEXT_MENU_IDS.SAVE_TO_SUPERMEMORY, title: "sync to supermemory", contexts: ["selection", "page", "link"], - }) + }); 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) => { @@ -56,27 +56,27 @@ export default defineBackground(() => { 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 @@ -85,14 +85,14 @@ 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 @@ -102,14 +102,14 @@ export default defineBackground(() => { 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 = { @@ -118,48 +118,48 @@ export default defineBackground(() => { data.content || `${data.highlightedText}\n\n${data.html}\n\n${data?.url}`, metadata: { sm_source: "consumer" }, - } + }; - const responseData = await saveMemory(payload) + 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, - }) + }); - 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 responseData = await searchMemories(data); const response = responseData as { - results?: Array<{ memory?: string }> - } - const memories: string[] = [] + results?: Array<{ memory?: string }>; + }; + const memories: string[] = []; response.results?.forEach((result, index) => { - memories.push(`${index + 1}. ${result.memory} \n`) - }) - console.log("Memories:", memories) - await trackEvent(eventSource) - return { success: true, data: memories } + memories.push(`${index + 1}. ${result.memory} \n`); + }); + console.log("Memories:", memories); + await trackEvent(eventSource); + return { success: true, data: memories }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "Unknown error", - } + }; } - } + }; /** * Handle extension messages @@ -172,83 +172,83 @@ 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, message.actionSource || "unknown", - ) - sendResponse(result) + ); + 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, message.actionSource || "unknown", - ) - sendResponse(result) + ); + sendResponse(result); } catch (error) { sendResponse({ success: false, error: error instanceof Error ? error.message : "Unknown error", - }) + }); } - })() - return true + })(); + return true; } if (message.action === MESSAGE_TYPES.CAPTURE_PROMPT) { - ;(async () => { + (async () => { try { const messageData = message.data as { - prompt: string - platform: string - source: string - } - console.log("=== PROMPT CAPTURED ===") - console.log(messageData) - console.log("========================") + prompt: string; + platform: string; + source: string; + }; + console.log("=== PROMPT CAPTURED ==="); + console.log(messageData); + console.log("========================"); const memoryData: MemoryData = { content: messageData.prompt, - } + }; const result = await saveMemoryToSupermemory( memoryData, `prompt_capture_${messageData.platform}`, - ) - sendResponse(result) + ); + 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/chatgpt.ts b/apps/browser-extension/entrypoints/content/chatgpt.ts index 73e0354f..4657acb7 100644 --- a/apps/browser-extension/entrypoints/content/chatgpt.ts +++ b/apps/browser-extension/entrypoints/content/chatgpt.ts @@ -5,78 +5,78 @@ import { POSTHOG_EVENT_KEY, STORAGE_KEYS, UI_CONFIG, -} from "../../utils/constants" +} from "../../utils/constants"; import { createChatGPTInputBarElement, DOMUtils, -} from "../../utils/ui-components" +} from "../../utils/ui-components"; -let chatGPTDebounceTimeout: NodeJS.Timeout | null = null -let chatGPTRouteObserver: MutationObserver | null = null -let chatGPTUrlCheckInterval: NodeJS.Timeout | null = null -let chatGPTObserverThrottle: NodeJS.Timeout | null = null +let chatGPTDebounceTimeout: NodeJS.Timeout | null = null; +let chatGPTRouteObserver: MutationObserver | null = null; +let chatGPTUrlCheckInterval: NodeJS.Timeout | null = null; +let chatGPTObserverThrottle: NodeJS.Timeout | null = null; export function initializeChatGPT() { if (!DOMUtils.isOnDomain(DOMAINS.CHATGPT)) { - return + return; } if (document.body.hasAttribute("data-chatgpt-initialized")) { - return + return; } setTimeout(() => { - addSupermemoryButtonToMemoriesDialog() - addSaveChatGPTElementBeforeComposerBtn() - setupChatGPTAutoFetch() - }, 2000) + addSupermemoryButtonToMemoriesDialog(); + addSaveChatGPTElementBeforeComposerBtn(); + setupChatGPTAutoFetch(); + }, 2000); - setupChatGPTPromptCapture() + setupChatGPTPromptCapture(); - setupChatGPTRouteChangeDetection() + setupChatGPTRouteChangeDetection(); - document.body.setAttribute("data-chatgpt-initialized", "true") + document.body.setAttribute("data-chatgpt-initialized", "true"); } function setupChatGPTRouteChangeDetection() { if (chatGPTRouteObserver) { - chatGPTRouteObserver.disconnect() + chatGPTRouteObserver.disconnect(); } if (chatGPTUrlCheckInterval) { - clearInterval(chatGPTUrlCheckInterval) + clearInterval(chatGPTUrlCheckInterval); } if (chatGPTObserverThrottle) { - clearTimeout(chatGPTObserverThrottle) - chatGPTObserverThrottle = null + clearTimeout(chatGPTObserverThrottle); + chatGPTObserverThrottle = null; } - let currentUrl = window.location.href + let currentUrl = window.location.href; const checkForRouteChange = () => { if (window.location.href !== currentUrl) { - currentUrl = window.location.href - console.log("ChatGPT route changed, re-adding supermemory elements") + currentUrl = window.location.href; + console.log("ChatGPT route changed, re-adding supermemory elements"); setTimeout(() => { - addSupermemoryButtonToMemoriesDialog() - addSaveChatGPTElementBeforeComposerBtn() - setupChatGPTAutoFetch() - }, 1000) + addSupermemoryButtonToMemoriesDialog(); + addSaveChatGPTElementBeforeComposerBtn(); + setupChatGPTAutoFetch(); + }, 1000); } - } + }; - chatGPTUrlCheckInterval = setInterval(checkForRouteChange, 2000) + chatGPTUrlCheckInterval = setInterval(checkForRouteChange, 2000); chatGPTRouteObserver = new MutationObserver((mutations) => { if (chatGPTObserverThrottle) { - return + return; } - let shouldRecheck = false + let shouldRecheck = false; mutations.forEach((mutation) => { if (mutation.type === "childList" && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { - const element = node as Element + const element = node as Element; if ( element.querySelector?.("#prompt-textarea") || element.querySelector?.("button.composer-btn") || @@ -84,65 +84,65 @@ function setupChatGPTRouteChangeDetection() { element.matches?.("#prompt-textarea") || element.id === "prompt-textarea" ) { - shouldRecheck = true + shouldRecheck = true; } } - }) + }); } - }) + }); if (shouldRecheck) { chatGPTObserverThrottle = setTimeout(() => { try { - chatGPTObserverThrottle = null - addSupermemoryButtonToMemoriesDialog() - addSaveChatGPTElementBeforeComposerBtn() - setupChatGPTAutoFetch() + chatGPTObserverThrottle = null; + addSupermemoryButtonToMemoriesDialog(); + addSaveChatGPTElementBeforeComposerBtn(); + setupChatGPTAutoFetch(); } catch (error) { - console.error("Error in ChatGPT observer callback:", error) + console.error("Error in ChatGPT observer callback:", error); } - }, 300) + }, 300); } - }) + }); try { chatGPTRouteObserver.observe(document.body, { childList: true, subtree: true, - }) + }); } catch (error) { - console.error("Failed to set up ChatGPT route observer:", error) + console.error("Failed to set up ChatGPT route observer:", error); if (chatGPTUrlCheckInterval) { - clearInterval(chatGPTUrlCheckInterval) + clearInterval(chatGPTUrlCheckInterval); } - chatGPTUrlCheckInterval = setInterval(checkForRouteChange, 1000) + chatGPTUrlCheckInterval = setInterval(checkForRouteChange, 1000); } } async function getRelatedMemoriesForChatGPT(actionSource: string) { try { const userQuery = - document.getElementById("prompt-textarea")?.textContent || "" + document.getElementById("prompt-textarea")?.textContent || ""; const icon = document.querySelectorAll( '[id*="sm-chatgpt-input-bar-element-before-composer"]', - )[0] + )[0]; - const iconElement = icon as HTMLElement + const iconElement = icon as HTMLElement; if (!iconElement) { - console.warn("ChatGPT icon element not found, cannot update feedback") - return + console.warn("ChatGPT icon element not found, cannot update feedback"); + return; } - updateChatGPTIconFeedback("Searching memories...", iconElement) + updateChatGPTIconFeedback("Searching memories...", iconElement); const timeoutPromise = new Promise((_, reject) => setTimeout( () => reject(new Error("Memory search timeout")), UI_CONFIG.API_REQUEST_TIMEOUT, ), - ) + ); const response = await Promise.race([ browser.runtime.sendMessage({ @@ -151,78 +151,78 @@ async function getRelatedMemoriesForChatGPT(actionSource: string) { actionSource: actionSource, }), timeoutPromise, - ]) + ]); if (response?.success && response?.data) { - const promptElement = document.getElementById("prompt-textarea") + const promptElement = document.getElementById("prompt-textarea"); if (promptElement) { - promptElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${response.data}</div>` + promptElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${response.data}</div>`; console.log( "Prompt element dataset:", promptElement.dataset.supermemories, - ) + ); - iconElement.dataset.memoriesData = response.data + iconElement.dataset.memoriesData = response.data; - updateChatGPTIconFeedback("Included Memories", iconElement) + updateChatGPTIconFeedback("Included Memories", iconElement); } else { console.warn( "ChatGPT prompt element not found after successful memory fetch", - ) - updateChatGPTIconFeedback("Memories found", iconElement) + ); + updateChatGPTIconFeedback("Memories found", iconElement); } } else { - console.warn("No memories found or API response invalid") - updateChatGPTIconFeedback("No memories found", iconElement) + console.warn("No memories found or API response invalid"); + updateChatGPTIconFeedback("No memories found", iconElement); } } catch (error) { - console.error("Error getting related memories:", error) + console.error("Error getting related memories:", error); try { const icon = document.querySelectorAll( '[id*="sm-chatgpt-input-bar-element-before-composer"]', - )[0] as HTMLElement + )[0] as HTMLElement; if (icon) { - updateChatGPTIconFeedback("Error fetching memories", icon) + updateChatGPTIconFeedback("Error fetching memories", icon); } } catch (feedbackError) { - console.error("Failed to update error feedback:", feedbackError) + console.error("Failed to update error feedback:", feedbackError); } } } 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; @@ -234,54 +234,54 @@ function addSupermemoryButtonToMemoriesDialog() { 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') + 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, @@ -289,18 +289,18 @@ async function saveMemoriesToSupermemory() { 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"); } } @@ -310,10 +310,10 @@ function updateChatGPTIconFeedback( resetAfter = 0, ) { if (!iconElement.dataset.originalHtml) { - iconElement.dataset.originalHtml = iconElement.innerHTML + iconElement.dataset.originalHtml = iconElement.innerHTML; } - const feedbackDiv = document.createElement("div") + const feedbackDiv = document.createElement("div"); feedbackDiv.style.cssText = ` display: flex; align-items: center; @@ -326,15 +326,15 @@ function updateChatGPTIconFeedback( font-weight: 500; cursor: ${message === "Included Memories" ? "pointer" : "default"}; position: relative; - ` + `; feedbackDiv.innerHTML = ` <span>✓</span> <span>${message}</span> - ` + `; if (message === "Included Memories" && iconElement.dataset.memoriesData) { - const popup = document.createElement("div") + const popup = document.createElement("div"); popup.style.cssText = ` position: fixed; bottom: 80px; @@ -352,9 +352,9 @@ function updateChatGPTIconFeedback( z-index: 999999; display: none; border: 1px solid #333; - ` + `; - const header = document.createElement("div") + const header = document.createElement("div"); header.style.cssText = ` display: flex; justify-content: space-between; @@ -362,28 +362,28 @@ function updateChatGPTIconFeedback( padding: 8px; border-bottom: 1px solid #333; opacity: 0.8; - ` + `; header.innerHTML = ` <span style="font-weight: 600; color: #fff;">Included Memories</span> - ` + `; - const content = document.createElement("div") + const content = document.createElement("div"); content.style.cssText = ` padding: 0; max-height: 300px; overflow-y: auto; - ` + `; - const memoriesText = iconElement.dataset.memoriesData || "" - console.log("Memories text:", memoriesText) + const memoriesText = iconElement.dataset.memoriesData || ""; + console.log("Memories text:", memoriesText); const individualMemories = memoriesText .split(/[,\n]/) .map((memory) => memory.trim()) - .filter((memory) => memory.length > 0 && memory !== ",") - console.log("Individual memories:", individualMemories) + .filter((memory) => memory.length > 0 && memory !== ","); + console.log("Individual memories:", individualMemories); individualMemories.forEach((memory, index) => { - const memoryItem = document.createElement("div") + const memoryItem = document.createElement("div"); memoryItem.style.cssText = ` display: flex; align-items: center; @@ -391,16 +391,16 @@ function updateChatGPTIconFeedback( padding: 10px; font-size: 13px; line-height: 1.4; - ` + `; - const memoryText = document.createElement("div") + const memoryText = document.createElement("div"); memoryText.style.cssText = ` flex: 1; color: #e5e5e5; - ` - memoryText.textContent = memory.trim() + `; + memoryText.textContent = memory.trim(); - const removeBtn = document.createElement("button") + const removeBtn = document.createElement("button"); removeBtn.style.cssText = ` background: transparent; color: #9ca3af; @@ -413,252 +413,255 @@ function updateChatGPTIconFeedback( display: flex; align-items: center; justify-content: center; - ` - removeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>` - removeBtn.dataset.memoryIndex = index.toString() + `; + removeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`; + removeBtn.dataset.memoryIndex = index.toString(); removeBtn.addEventListener("mouseenter", () => { - removeBtn.style.color = "#ef4444" - }) + removeBtn.style.color = "#ef4444"; + }); removeBtn.addEventListener("mouseleave", () => { - removeBtn.style.color = "#9ca3af" - }) + removeBtn.style.color = "#9ca3af"; + }); - memoryItem.appendChild(memoryText) - memoryItem.appendChild(removeBtn) - content.appendChild(memoryItem) - }) + memoryItem.appendChild(memoryText); + memoryItem.appendChild(removeBtn); + content.appendChild(memoryItem); + }); - popup.appendChild(header) - popup.appendChild(content) - document.body.appendChild(popup) + popup.appendChild(header); + popup.appendChild(content); + document.body.appendChild(popup); feedbackDiv.addEventListener("mouseenter", () => { - const textSpan = feedbackDiv.querySelector("span:last-child") + const textSpan = feedbackDiv.querySelector("span:last-child"); if (textSpan) { - textSpan.textContent = "Click to see memories" + textSpan.textContent = "Click to see memories"; } - }) + }); feedbackDiv.addEventListener("mouseleave", () => { - const textSpan = feedbackDiv.querySelector("span:last-child") + const textSpan = feedbackDiv.querySelector("span:last-child"); if (textSpan) { - textSpan.textContent = "Included Memories" + textSpan.textContent = "Included Memories"; } - }) + }); feedbackDiv.addEventListener("click", (e) => { - e.stopPropagation() - popup.style.display = "block" - }) + e.stopPropagation(); + popup.style.display = "block"; + }); document.addEventListener("click", (e) => { if (!popup.contains(e.target as Node)) { - popup.style.display = "none" + popup.style.display = "none"; } - }) + }); content.querySelectorAll("button[data-memory-index]").forEach((button) => { - const htmlButton = button as HTMLButtonElement + const htmlButton = button as HTMLButtonElement; htmlButton.addEventListener("click", () => { - const index = Number.parseInt(htmlButton.dataset.memoryIndex || "0", 10) - const memoryItem = htmlButton.parentElement + const index = Number.parseInt( + htmlButton.dataset.memoryIndex || "0", + 10, + ); + const memoryItem = htmlButton.parentElement; if (memoryItem) { - content.removeChild(memoryItem) + content.removeChild(memoryItem); } const currentMemories = (iconElement.dataset.memoriesData || "") .split(/[,\n]/) .map((memory) => memory.trim()) - .filter((memory) => memory.length > 0 && memory !== ",") - currentMemories.splice(index, 1) + .filter((memory) => memory.length > 0 && memory !== ","); + currentMemories.splice(index, 1); - const updatedMemories = currentMemories.join(" ,") + const updatedMemories = currentMemories.join(" ,"); - iconElement.dataset.memoriesData = updatedMemories + iconElement.dataset.memoriesData = updatedMemories; - const promptElement = document.getElementById("prompt-textarea") + const promptElement = document.getElementById("prompt-textarea"); if (promptElement) { - promptElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${updatedMemories}</div>` + promptElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${updatedMemories}</div>`; } content .querySelectorAll("button[data-memory-index]") .forEach((btn, newIndex) => { - const htmlBtn = btn as HTMLButtonElement - htmlBtn.dataset.memoryIndex = newIndex.toString() - }) + const htmlBtn = btn as HTMLButtonElement; + htmlBtn.dataset.memoryIndex = newIndex.toString(); + }); if (currentMemories.length <= 1) { if (promptElement?.dataset.supermemories) { - delete promptElement.dataset.supermemories - delete iconElement.dataset.memoriesData - iconElement.innerHTML = iconElement.dataset.originalHtml || "" - delete iconElement.dataset.originalHtml + delete promptElement.dataset.supermemories; + delete iconElement.dataset.memoriesData; + iconElement.innerHTML = iconElement.dataset.originalHtml || ""; + delete iconElement.dataset.originalHtml; } - popup.style.display = "none" + popup.style.display = "none"; if (document.body.contains(popup)) { - document.body.removeChild(popup) + document.body.removeChild(popup); } } - }) - }) + }); + }); setTimeout(() => { if (document.body.contains(popup)) { - document.body.removeChild(popup) + document.body.removeChild(popup); } - }, 300000) + }, 300000); } - iconElement.innerHTML = "" - iconElement.appendChild(feedbackDiv) + iconElement.innerHTML = ""; + iconElement.appendChild(feedbackDiv); if (resetAfter > 0) { setTimeout(() => { - iconElement.innerHTML = iconElement.dataset.originalHtml || "" - delete iconElement.dataset.originalHtml - }, resetAfter) + iconElement.innerHTML = iconElement.dataset.originalHtml || ""; + delete iconElement.dataset.originalHtml; + }, resetAfter); } } function addSaveChatGPTElementBeforeComposerBtn() { - 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 getRelatedMemoriesForChatGPT( 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); - setupChatGPTAutoFetch() - }) + setupChatGPTAutoFetch(); + }); } async function setupChatGPTAutoFetch() { const result = await chrome.storage.local.get([ STORAGE_KEYS.AUTO_SEARCH_ENABLED, - ]) - const autoSearchEnabled = result[STORAGE_KEYS.AUTO_SEARCH_ENABLED] ?? false + ]); + const autoSearchEnabled = result[STORAGE_KEYS.AUTO_SEARCH_ENABLED] ?? false; if (!autoSearchEnabled) { - return + return; } - const promptTextarea = document.getElementById("prompt-textarea") + const promptTextarea = document.getElementById("prompt-textarea"); if ( !promptTextarea || promptTextarea.hasAttribute("data-supermemory-auto-fetch") ) { - return + return; } - promptTextarea.setAttribute("data-supermemory-auto-fetch", "true") + promptTextarea.setAttribute("data-supermemory-auto-fetch", "true"); const handleInput = () => { if (chatGPTDebounceTimeout) { - clearTimeout(chatGPTDebounceTimeout) + clearTimeout(chatGPTDebounceTimeout); } chatGPTDebounceTimeout = setTimeout(async () => { - const content = promptTextarea.textContent?.trim() || "" + const content = promptTextarea.textContent?.trim() || ""; if (content.length > 2) { await getRelatedMemoriesForChatGPT( POSTHOG_EVENT_KEY.CHATGPT_CHAT_MEMORIES_AUTO_SEARCHED, - ) + ); } else if (content.length === 0) { const icons = document.querySelectorAll( '[id*="sm-chatgpt-input-bar-element-before-composer"]', - ) + ); icons.forEach((icon) => { - const iconElement = icon as HTMLElement + const iconElement = icon as HTMLElement; if (iconElement.dataset.originalHtml) { - iconElement.innerHTML = iconElement.dataset.originalHtml - delete iconElement.dataset.originalHtml - delete iconElement.dataset.memoriesData + iconElement.innerHTML = iconElement.dataset.originalHtml; + delete iconElement.dataset.originalHtml; + delete iconElement.dataset.memoriesData; } - }) + }); if (promptTextarea.dataset.supermemories) { - delete promptTextarea.dataset.supermemories + delete promptTextarea.dataset.supermemories; } } - }, UI_CONFIG.AUTO_SEARCH_DEBOUNCE_DELAY) - } + }, UI_CONFIG.AUTO_SEARCH_DEBOUNCE_DELAY); + }; - promptTextarea.addEventListener("input", handleInput) + promptTextarea.addEventListener("input", handleInput); } function setupChatGPTPromptCapture() { if (document.body.hasAttribute("data-chatgpt-prompt-capture-setup")) { - return + return; } - document.body.setAttribute("data-chatgpt-prompt-capture-setup", "true") + document.body.setAttribute("data-chatgpt-prompt-capture-setup", "true"); const capturePromptContent = async (source: string) => { - const promptTextarea = document.getElementById("prompt-textarea") + const promptTextarea = document.getElementById("prompt-textarea"); - let promptContent = "" + let promptContent = ""; if (promptTextarea) { - promptContent = promptTextarea.textContent || "" + promptContent = promptTextarea.textContent || ""; } - const storedMemories = promptTextarea?.dataset.supermemories + const storedMemories = promptTextarea?.dataset.supermemories; if ( storedMemories && promptTextarea && !promptContent.includes("Supermemories of user") ) { - promptTextarea.innerHTML = `${promptTextarea.innerHTML} ${storedMemories}` - promptContent = promptTextarea.textContent || "" + promptTextarea.innerHTML = `${promptTextarea.innerHTML} ${storedMemories}`; + promptContent = promptTextarea.textContent || ""; } if (promptTextarea && promptContent.trim()) { - console.log(`ChatGPT prompt submitted via ${source}:`, promptContent) + console.log(`ChatGPT prompt submitted via ${source}:`, promptContent); try { await browser.runtime.sendMessage({ @@ -668,57 +671,57 @@ function setupChatGPTPromptCapture() { platform: "chatgpt", source: source, }, - }) + }); } catch (error) { - console.error("Error sending ChatGPT prompt to background:", error) + console.error("Error sending ChatGPT prompt to background:", error); } } const icons = document.querySelectorAll( '[id*="sm-chatgpt-input-bar-element-before-composer"]', - ) + ); icons.forEach((icon) => { - const iconElement = icon as HTMLElement + const iconElement = icon as HTMLElement; if (iconElement.dataset.originalHtml) { - iconElement.innerHTML = iconElement.dataset.originalHtml - delete iconElement.dataset.originalHtml - delete iconElement.dataset.memoriesData + iconElement.innerHTML = iconElement.dataset.originalHtml; + delete iconElement.dataset.originalHtml; + delete iconElement.dataset.memoriesData; } - }) + }); if (promptTextarea?.dataset.supermemories) { - delete promptTextarea.dataset.supermemories + delete promptTextarea.dataset.supermemories; } - } + }; document.addEventListener( "click", async (event) => { - const target = event.target as HTMLElement + const target = event.target as HTMLElement; if ( target.id === "composer-submit-button" || target.closest("#composer-submit-button") ) { - await capturePromptContent("button click") + await capturePromptContent("button click"); } }, true, - ) + ); document.addEventListener( "keydown", async (event) => { - const target = event.target as HTMLElement + const target = event.target as HTMLElement; if ( target.id === "prompt-textarea" && event.key === "Enter" && !event.shiftKey ) { - await capturePromptContent("Enter key") + await capturePromptContent("Enter key"); } }, true, - ) + ); } diff --git a/apps/browser-extension/entrypoints/content/claude.ts b/apps/browser-extension/entrypoints/content/claude.ts index e0853d41..e5832bf7 100644 --- a/apps/browser-extension/entrypoints/content/claude.ts +++ b/apps/browser-extension/entrypoints/content/claude.ts @@ -5,210 +5,210 @@ import { POSTHOG_EVENT_KEY, STORAGE_KEYS, UI_CONFIG, -} from "../../utils/constants" +} from "../../utils/constants"; import { createClaudeInputBarElement, DOMUtils, -} from "../../utils/ui-components" +} from "../../utils/ui-components"; -let claudeDebounceTimeout: NodeJS.Timeout | null = null -let claudeRouteObserver: MutationObserver | null = null -let claudeUrlCheckInterval: NodeJS.Timeout | null = null -let claudeObserverThrottle: NodeJS.Timeout | null = null +let claudeDebounceTimeout: NodeJS.Timeout | null = null; +let claudeRouteObserver: MutationObserver | null = null; +let claudeUrlCheckInterval: NodeJS.Timeout | null = null; +let claudeObserverThrottle: NodeJS.Timeout | null = null; export function initializeClaude() { if (!DOMUtils.isOnDomain(DOMAINS.CLAUDE)) { - return + return; } if (document.body.hasAttribute("data-claude-initialized")) { - return + return; } setTimeout(() => { - addSupermemoryIconToClaudeInput() - setupClaudeAutoFetch() - }, 2000) + addSupermemoryIconToClaudeInput(); + setupClaudeAutoFetch(); + }, 2000); - setupClaudePromptCapture() + setupClaudePromptCapture(); - setupClaudeRouteChangeDetection() + setupClaudeRouteChangeDetection(); - document.body.setAttribute("data-claude-initialized", "true") + document.body.setAttribute("data-claude-initialized", "true"); } function setupClaudeRouteChangeDetection() { if (claudeRouteObserver) { - claudeRouteObserver.disconnect() + claudeRouteObserver.disconnect(); } if (claudeUrlCheckInterval) { - clearInterval(claudeUrlCheckInterval) + clearInterval(claudeUrlCheckInterval); } if (claudeObserverThrottle) { - clearTimeout(claudeObserverThrottle) - claudeObserverThrottle = null + clearTimeout(claudeObserverThrottle); + claudeObserverThrottle = null; } - let currentUrl = window.location.href + let currentUrl = window.location.href; const checkForRouteChange = () => { if (window.location.href !== currentUrl) { - currentUrl = window.location.href - console.log("Claude route changed, re-adding supermemory icon") + currentUrl = window.location.href; + console.log("Claude route changed, re-adding supermemory icon"); setTimeout(() => { - addSupermemoryIconToClaudeInput() - setupClaudeAutoFetch() - }, 1000) + addSupermemoryIconToClaudeInput(); + setupClaudeAutoFetch(); + }, 1000); } - } + }; - claudeUrlCheckInterval = setInterval(checkForRouteChange, 2000) + claudeUrlCheckInterval = setInterval(checkForRouteChange, 2000); claudeRouteObserver = new MutationObserver((mutations) => { if (claudeObserverThrottle) { - return + return; } - let shouldRecheck = false + let shouldRecheck = false; mutations.forEach((mutation) => { if (mutation.type === "childList" && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { - const element = node as Element + const element = node as Element; if ( element.querySelector?.('div[contenteditable="true"]') || element.querySelector?.("textarea") || element.matches?.('div[contenteditable="true"]') || element.matches?.("textarea") ) { - shouldRecheck = true + shouldRecheck = true; } } - }) + }); } - }) + }); if (shouldRecheck) { claudeObserverThrottle = setTimeout(() => { try { - claudeObserverThrottle = null - addSupermemoryIconToClaudeInput() - setupClaudeAutoFetch() + claudeObserverThrottle = null; + addSupermemoryIconToClaudeInput(); + setupClaudeAutoFetch(); } catch (error) { - console.error("Error in Claude observer callback:", error) + console.error("Error in Claude observer callback:", error); } - }, 300) + }, 300); } - }) + }); try { claudeRouteObserver.observe(document.body, { childList: true, subtree: true, - }) + }); } catch (error) { - console.error("Failed to set up Claude route observer:", error) + console.error("Failed to set up Claude route observer:", error); if (claudeUrlCheckInterval) { - clearInterval(claudeUrlCheckInterval) + clearInterval(claudeUrlCheckInterval); } - claudeUrlCheckInterval = setInterval(checkForRouteChange, 1000) + claudeUrlCheckInterval = setInterval(checkForRouteChange, 1000); } } function addSupermemoryIconToClaudeInput() { 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( POSTHOG_EVENT_KEY.CLAUDE_CHAT_MEMORIES_SEARCHED, - ) - }) + ); + }); - 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(actionSource: string) { 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 for Claude") - return + console.log("No query text found for Claude"); + return; } - const icon = document.querySelector('[id*="sm-claude-input-bar-element"]') + const icon = document.querySelector('[id*="sm-claude-input-bar-element"]'); - const iconElement = icon as HTMLElement + const iconElement = icon as HTMLElement; if (!iconElement) { - console.warn("Claude icon element not found, cannot update feedback") - return + console.warn("Claude icon element not found, cannot update feedback"); + return; } - updateClaudeIconFeedback("Searching memories...", iconElement) + updateClaudeIconFeedback("Searching memories...", iconElement); const timeoutPromise = new Promise((_, reject) => setTimeout( () => reject(new Error("Memory search timeout")), UI_CONFIG.API_REQUEST_TIMEOUT, ), - ) + ); const response = await Promise.race([ browser.runtime.sendMessage({ @@ -217,46 +217,46 @@ async function getRelatedMemoriesForClaude(actionSource: string) { actionSource: actionSource, }), timeoutPromise, - ]) + ]); - 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) { - textareaElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${response.data}</div>` + textareaElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${response.data}</div>`; console.log( "Text element dataset:", textareaElement.dataset.supermemories, - ) + ); - iconElement.dataset.memoriesData = response.data + iconElement.dataset.memoriesData = response.data; - updateClaudeIconFeedback("Included Memories", iconElement) + updateClaudeIconFeedback("Included Memories", iconElement); } else { console.warn( "Claude input area not found after successful memory fetch", - ) - updateClaudeIconFeedback("Memories found", iconElement) + ); + updateClaudeIconFeedback("Memories found", iconElement); } } else { - console.warn("No memories found or API response invalid for Claude") - updateClaudeIconFeedback("No memories found", iconElement) + console.warn("No memories found or API response invalid for Claude"); + updateClaudeIconFeedback("No memories found", iconElement); } } catch (error) { - console.error("Error getting related memories for Claude:", error) + console.error("Error getting related memories for Claude:", error); try { const icon = document.querySelector( '[id*="sm-claude-input-bar-element"]', - ) as HTMLElement + ) as HTMLElement; if (icon) { - updateClaudeIconFeedback("Error fetching memories", icon) + updateClaudeIconFeedback("Error fetching memories", icon); } } catch (feedbackError) { - console.error("Failed to update Claude error feedback:", feedbackError) + console.error("Failed to update Claude error feedback:", feedbackError); } } } @@ -267,10 +267,10 @@ function updateClaudeIconFeedback( resetAfter = 0, ) { if (!iconElement.dataset.originalHtml) { - iconElement.dataset.originalHtml = iconElement.innerHTML + iconElement.dataset.originalHtml = iconElement.innerHTML; } - const feedbackDiv = document.createElement("div") + const feedbackDiv = document.createElement("div"); feedbackDiv.style.cssText = ` display: flex; align-items: center; @@ -283,15 +283,15 @@ function updateClaudeIconFeedback( font-weight: 500; cursor: ${message === "Included Memories" ? "pointer" : "default"}; position: relative; - ` + `; feedbackDiv.innerHTML = ` <span>✓</span> <span>${message}</span> - ` + `; if (message === "Included Memories" && iconElement.dataset.memoriesData) { - const popup = document.createElement("div") + const popup = document.createElement("div"); popup.style.cssText = ` position: fixed; bottom: 80px; @@ -309,9 +309,9 @@ function updateClaudeIconFeedback( z-index: 999999; display: none; border: 1px solid #333; - ` + `; - const header = document.createElement("div") + const header = document.createElement("div"); header.style.cssText = ` display: flex; justify-content: space-between; @@ -319,28 +319,28 @@ function updateClaudeIconFeedback( padding: 8px; border-bottom: 1px solid #333; opacity: 0.8; - ` + `; header.innerHTML = ` <span style="font-weight: 600; color: #fff;">Included Memories</span> - ` + `; - const content = document.createElement("div") + const content = document.createElement("div"); content.style.cssText = ` padding: 0; max-height: 300px; overflow-y: auto; - ` + `; - const memoriesText = iconElement.dataset.memoriesData || "" - console.log("Memories text:", memoriesText) + const memoriesText = iconElement.dataset.memoriesData || ""; + console.log("Memories text:", memoriesText); const individualMemories = memoriesText .split(/[,\n]/) .map((memory) => memory.trim()) - .filter((memory) => memory.length > 0 && memory !== ",") - console.log("Individual memories:", individualMemories) + .filter((memory) => memory.length > 0 && memory !== ","); + console.log("Individual memories:", individualMemories); individualMemories.forEach((memory, index) => { - const memoryItem = document.createElement("div") + const memoryItem = document.createElement("div"); memoryItem.style.cssText = ` display: flex; align-items: center; @@ -348,16 +348,16 @@ function updateClaudeIconFeedback( padding: 10px; font-size: 13px; line-height: 1.4; - ` + `; - const memoryText = document.createElement("div") + const memoryText = document.createElement("div"); memoryText.style.cssText = ` flex: 1; color: #e5e5e5; - ` - memoryText.textContent = memory.trim() + `; + memoryText.textContent = memory.trim(); - const removeBtn = document.createElement("button") + const removeBtn = document.createElement("button"); removeBtn.style.cssText = ` background: transparent; color: #9ca3af; @@ -370,154 +370,159 @@ function updateClaudeIconFeedback( display: flex; align-items: center; justify-content: center; - ` - removeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>` - removeBtn.dataset.memoryIndex = index.toString() + `; + removeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`; + removeBtn.dataset.memoryIndex = index.toString(); removeBtn.addEventListener("mouseenter", () => { - removeBtn.style.color = "#ef4444" - }) + removeBtn.style.color = "#ef4444"; + }); removeBtn.addEventListener("mouseleave", () => { - removeBtn.style.color = "#9ca3af" - }) + removeBtn.style.color = "#9ca3af"; + }); - memoryItem.appendChild(memoryText) - memoryItem.appendChild(removeBtn) - content.appendChild(memoryItem) - }) + memoryItem.appendChild(memoryText); + memoryItem.appendChild(removeBtn); + content.appendChild(memoryItem); + }); - popup.appendChild(header) - popup.appendChild(content) - document.body.appendChild(popup) + popup.appendChild(header); + popup.appendChild(content); + document.body.appendChild(popup); feedbackDiv.addEventListener("mouseenter", () => { - const textSpan = feedbackDiv.querySelector("span:last-child") + const textSpan = feedbackDiv.querySelector("span:last-child"); if (textSpan) { - textSpan.textContent = "Click to see memories" + textSpan.textContent = "Click to see memories"; } - }) + }); feedbackDiv.addEventListener("mouseleave", () => { - const textSpan = feedbackDiv.querySelector("span:last-child") + const textSpan = feedbackDiv.querySelector("span:last-child"); if (textSpan) { - textSpan.textContent = "Included Memories" + textSpan.textContent = "Included Memories"; } - }) + }); feedbackDiv.addEventListener("click", (e) => { - e.stopPropagation() - popup.style.display = "block" - }) + e.stopPropagation(); + popup.style.display = "block"; + }); document.addEventListener("click", (e) => { if (!popup.contains(e.target as Node)) { - popup.style.display = "none" + popup.style.display = "none"; } - }) + }); content.querySelectorAll("button[data-memory-index]").forEach((button) => { - const htmlButton = button as HTMLButtonElement + const htmlButton = button as HTMLButtonElement; htmlButton.addEventListener("click", () => { - const index = Number.parseInt(htmlButton.dataset.memoryIndex || "0", 10) - const memoryItem = htmlButton.parentElement + const index = Number.parseInt( + htmlButton.dataset.memoryIndex || "0", + 10, + ); + const memoryItem = htmlButton.parentElement; if (memoryItem) { - content.removeChild(memoryItem) + content.removeChild(memoryItem); } const currentMemories = (iconElement.dataset.memoriesData || "") .split(/[,\n]/) .map((memory) => memory.trim()) - .filter((memory) => memory.length > 0 && memory !== ",") - currentMemories.splice(index, 1) + .filter((memory) => memory.length > 0 && memory !== ","); + currentMemories.splice(index, 1); - const updatedMemories = currentMemories.join(" ,") + const updatedMemories = currentMemories.join(" ,"); - iconElement.dataset.memoriesData = updatedMemories + iconElement.dataset.memoriesData = updatedMemories; const textareaElement = document.querySelector( 'div[contenteditable="true"]', - ) as HTMLElement + ) as HTMLElement; if (textareaElement) { - textareaElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${updatedMemories}</div>` + textareaElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${updatedMemories}</div>`; } content .querySelectorAll("button[data-memory-index]") .forEach((btn, newIndex) => { - const htmlBtn = btn as HTMLButtonElement - htmlBtn.dataset.memoryIndex = newIndex.toString() - }) + const htmlBtn = btn as HTMLButtonElement; + htmlBtn.dataset.memoryIndex = newIndex.toString(); + }); if (currentMemories.length <= 1) { if (textareaElement?.dataset.supermemories) { - delete textareaElement.dataset.supermemories - delete iconElement.dataset.memoriesData - iconElement.innerHTML = iconElement.dataset.originalHtml || "" - delete iconElement.dataset.originalHtml + delete textareaElement.dataset.supermemories; + delete iconElement.dataset.memoriesData; + iconElement.innerHTML = iconElement.dataset.originalHtml || ""; + delete iconElement.dataset.originalHtml; } - popup.style.display = "none" + popup.style.display = "none"; if (document.body.contains(popup)) { - document.body.removeChild(popup) + document.body.removeChild(popup); } } - }) - }) + }); + }); setTimeout(() => { if (document.body.contains(popup)) { - document.body.removeChild(popup) + document.body.removeChild(popup); } - }, 300000) + }, 300000); } - iconElement.innerHTML = "" - iconElement.appendChild(feedbackDiv) + iconElement.innerHTML = ""; + iconElement.appendChild(feedbackDiv); if (resetAfter > 0) { setTimeout(() => { - iconElement.innerHTML = iconElement.dataset.originalHtml || "" - delete iconElement.dataset.originalHtml - }, resetAfter) + iconElement.innerHTML = iconElement.dataset.originalHtml || ""; + delete iconElement.dataset.originalHtml; + }, resetAfter); } } function setupClaudePromptCapture() { if (document.body.hasAttribute("data-claude-prompt-capture-setup")) { - return + return; } - document.body.setAttribute("data-claude-prompt-capture-setup", "true") + document.body.setAttribute("data-claude-prompt-capture-setup", "true"); const captureClaudePromptContent = async (source: string) => { - let promptContent = "" + let promptContent = ""; const contentEditableDiv = document.querySelector( 'div[contenteditable="true"]', - ) as HTMLElement + ) as HTMLElement; if (contentEditableDiv) { promptContent = - contentEditableDiv.textContent || contentEditableDiv.innerText || "" + contentEditableDiv.textContent || contentEditableDiv.innerText || ""; } if (!promptContent) { - const textarea = document.querySelector("textarea") as HTMLTextAreaElement + const textarea = document.querySelector( + "textarea", + ) as HTMLTextAreaElement; if (textarea) { - promptContent = textarea.value || "" + promptContent = textarea.value || ""; } } - const storedMemories = contentEditableDiv?.dataset.supermemories + const storedMemories = contentEditableDiv?.dataset.supermemories; if ( storedMemories && contentEditableDiv && !promptContent.includes("Supermemories of user") ) { - contentEditableDiv.innerHTML = `${contentEditableDiv.innerHTML} ${storedMemories}` + contentEditableDiv.innerHTML = `${contentEditableDiv.innerHTML} ${storedMemories}`; promptContent = - contentEditableDiv.textContent || contentEditableDiv.innerText || "" + contentEditableDiv.textContent || contentEditableDiv.innerText || ""; } if (promptContent.trim()) { - console.log(`Claude prompt submitted via ${source}:`, promptContent) + console.log(`Claude prompt submitted via ${source}:`, promptContent); try { await browser.runtime.sendMessage({ @@ -527,52 +532,52 @@ function setupClaudePromptCapture() { platform: "claude", source: source, }, - }) + }); } catch (error) { - console.error("Error sending Claude prompt to background:", error) + console.error("Error sending Claude prompt to background:", error); } } const icons = document.querySelectorAll( '[id*="sm-claude-input-bar-element"]', - ) + ); icons.forEach((icon) => { - const iconElement = icon as HTMLElement + const iconElement = icon as HTMLElement; if (iconElement.dataset.originalHtml) { - iconElement.innerHTML = iconElement.dataset.originalHtml - delete iconElement.dataset.originalHtml - delete iconElement.dataset.memoriesData + iconElement.innerHTML = iconElement.dataset.originalHtml; + delete iconElement.dataset.originalHtml; + delete iconElement.dataset.memoriesData; } - }) + }); if (contentEditableDiv?.dataset.supermemories) { - delete contentEditableDiv.dataset.supermemories + delete contentEditableDiv.dataset.supermemories; } - } + }; document.addEventListener( "click", async (event) => { - const target = event.target as HTMLElement + const target = event.target as HTMLElement; const sendButton = target.closest( "button.inline-flex.items-center.justify-center.relative.shrink-0.can-focus.select-none", ) || target.closest('button[class*="bg-accent-main-000"]') || - target.closest('button[class*="rounded-lg"]') + target.closest('button[class*="rounded-lg"]'); if (sendButton) { - await captureClaudePromptContent("button click") + await captureClaudePromptContent("button click"); } }, true, - ) + ); document.addEventListener( "keydown", async (event) => { - const target = event.target as HTMLElement + const target = event.target as HTMLElement; if ( (target.matches('div[contenteditable="true"]') || @@ -582,67 +587,67 @@ function setupClaudePromptCapture() { event.key === "Enter" && !event.shiftKey ) { - await captureClaudePromptContent("Enter key") + await captureClaudePromptContent("Enter key"); } }, true, - ) + ); } async function setupClaudeAutoFetch() { const result = await chrome.storage.local.get([ STORAGE_KEYS.AUTO_SEARCH_ENABLED, - ]) - const autoSearchEnabled = result[STORAGE_KEYS.AUTO_SEARCH_ENABLED] ?? false + ]); + const autoSearchEnabled = result[STORAGE_KEYS.AUTO_SEARCH_ENABLED] ?? false; if (!autoSearchEnabled) { - return + return; } const textareaElement = document.querySelector( 'div[contenteditable="true"]', - ) as HTMLElement + ) as HTMLElement; if ( !textareaElement || textareaElement.hasAttribute("data-supermemory-auto-fetch") ) { - return + return; } - textareaElement.setAttribute("data-supermemory-auto-fetch", "true") + textareaElement.setAttribute("data-supermemory-auto-fetch", "true"); const handleInput = () => { if (claudeDebounceTimeout) { - clearTimeout(claudeDebounceTimeout) + clearTimeout(claudeDebounceTimeout); } claudeDebounceTimeout = setTimeout(async () => { - const content = textareaElement.textContent?.trim() || "" + const content = textareaElement.textContent?.trim() || ""; if (content.length > 2) { await getRelatedMemoriesForClaude( POSTHOG_EVENT_KEY.CLAUDE_CHAT_MEMORIES_AUTO_SEARCHED, - ) + ); } else if (content.length === 0) { const icons = document.querySelectorAll( '[id*="sm-claude-input-bar-element"]', - ) + ); icons.forEach((icon) => { - const iconElement = icon as HTMLElement + const iconElement = icon as HTMLElement; if (iconElement.dataset.originalHtml) { - iconElement.innerHTML = iconElement.dataset.originalHtml - delete iconElement.dataset.originalHtml - delete iconElement.dataset.memoriesData + iconElement.innerHTML = iconElement.dataset.originalHtml; + delete iconElement.dataset.originalHtml; + delete iconElement.dataset.memoriesData; } - }) + }); if (textareaElement.dataset.supermemories) { - delete textareaElement.dataset.supermemories + delete textareaElement.dataset.supermemories; } } - }, UI_CONFIG.AUTO_SEARCH_DEBOUNCE_DELAY) - } + }, UI_CONFIG.AUTO_SEARCH_DEBOUNCE_DELAY); + }; - textareaElement.addEventListener("input", handleInput) + textareaElement.addEventListener("input", handleInput); } diff --git a/apps/browser-extension/entrypoints/content/index.ts b/apps/browser-extension/entrypoints/content/index.ts index a79f50fb..829c2fa9 100644 --- a/apps/browser-extension/entrypoints/content/index.ts +++ b/apps/browser-extension/entrypoints/content/index.ts @@ -1,10 +1,18 @@ -import { DOMAINS, MESSAGE_TYPES } from "../../utils/constants" -import { DOMUtils } from "../../utils/ui-components" -import { initializeChatGPT } from "./chatgpt" -import { initializeClaude } from "./claude" -import { saveMemory, setupGlobalKeyboardShortcut, setupStorageListener } from "./shared" -import { initializeT3 } from "./t3" -import { handleTwitterNavigation, initializeTwitter, updateTwitterImportUI } from "./twitter" +import { DOMAINS, MESSAGE_TYPES } from "../../utils/constants"; +import { DOMUtils } from "../../utils/ui-components"; +import { initializeChatGPT } from "./chatgpt"; +import { initializeClaude } from "./claude"; +import { + saveMemory, + setupGlobalKeyboardShortcut, + setupStorageListener, +} from "./shared"; +import { initializeT3 } from "./t3"; +import { + handleTwitterNavigation, + initializeTwitter, + updateTwitterImportUI, +} from "./twitter"; export default defineContentScript({ matches: ["<all_urls>"], @@ -12,56 +20,56 @@ export default defineContentScript({ // Setup global event listeners 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); } - }) + }); // Setup global keyboard shortcuts - setupGlobalKeyboardShortcut() + setupGlobalKeyboardShortcut(); // Setup storage listener - setupStorageListener() + setupStorageListener(); // Observer for dynamic content changes const observeForDynamicChanges = () => { const observer = new MutationObserver(() => { if (DOMUtils.isOnDomain(DOMAINS.CHATGPT)) { - initializeChatGPT() + initializeChatGPT(); } if (DOMUtils.isOnDomain(DOMAINS.CLAUDE)) { - initializeClaude() + initializeClaude(); } if (DOMUtils.isOnDomain(DOMAINS.T3)) { - initializeT3() + initializeT3(); } if (DOMUtils.isOnDomain(DOMAINS.TWITTER)) { - handleTwitterNavigation() + handleTwitterNavigation(); } - }) + }); observer.observe(document.body, { childList: true, subtree: true, - }) - } + }); + }; // Initialize platform-specific functionality - initializeChatGPT() - initializeClaude() - initializeT3() - initializeTwitter() + initializeChatGPT(); + initializeClaude(); + initializeT3(); + initializeTwitter(); // Start observing for dynamic changes if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", observeForDynamicChanges) + document.addEventListener("DOMContentLoaded", observeForDynamicChanges); } else { - observeForDynamicChanges() + observeForDynamicChanges(); } }, -})
\ No newline at end of file +}); diff --git a/apps/browser-extension/entrypoints/content/shared.ts b/apps/browser-extension/entrypoints/content/shared.ts index d8b665c5..8c6aae65 100644 --- a/apps/browser-extension/entrypoints/content/shared.ts +++ b/apps/browser-extension/entrypoints/content/shared.ts @@ -1,13 +1,13 @@ -import { MESSAGE_TYPES, STORAGE_KEYS } from "../../utils/constants" -import { DOMUtils } from "../../utils/ui-components" +import { MESSAGE_TYPES, STORAGE_KEYS } from "../../utils/constants"; +import { DOMUtils } from "../../utils/ui-components"; export async function saveMemory() { try { - DOMUtils.showToast("loading") + DOMUtils.showToast("loading"); - const highlightedText = window.getSelection()?.toString() || "" - const url = window.location.href - const html = document.documentElement.outerHTML + const highlightedText = window.getSelection()?.toString() || ""; + const url = window.location.href; + const html = document.documentElement.outerHTML; const response = await browser.runtime.sendMessage({ action: MESSAGE_TYPES.SAVE_MEMORY, @@ -17,17 +17,17 @@ export async function saveMemory() { 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"); } } @@ -38,19 +38,19 @@ export function setupGlobalKeyboardShortcut() { event.shiftKey && event.key === "m" ) { - event.preventDefault() - await saveMemory() + event.preventDefault(); + await saveMemory(); } - }) + }); } export function setupStorageListener() { window.addEventListener("message", (event) => { if (event.source !== window) { - return + return; } - const bearerToken = event.data.token - const userData = event.data.userData + const bearerToken = event.data.token; + const userData = event.data.userData; if (bearerToken && userData) { if ( !( @@ -61,8 +61,8 @@ export function setupStorageListener() { ) { console.log( "Bearer token and user data is only allowed to be used on localhost or supermemory.ai", - ) - return + ); + return; } chrome.storage.local.set( @@ -71,7 +71,7 @@ export function setupStorageListener() { [STORAGE_KEYS.USER_DATA]: userData, }, () => {}, - ) + ); } - }) -}
\ No newline at end of file + }); +} diff --git a/apps/browser-extension/entrypoints/content/t3.ts b/apps/browser-extension/entrypoints/content/t3.ts index 4332076e..d630ffe5 100644 --- a/apps/browser-extension/entrypoints/content/t3.ts +++ b/apps/browser-extension/entrypoints/content/t3.ts @@ -5,204 +5,206 @@ import { POSTHOG_EVENT_KEY, STORAGE_KEYS, UI_CONFIG, -} from "../../utils/constants" -import { createT3InputBarElement, DOMUtils } from "../../utils/ui-components" +} from "../../utils/constants"; +import { createT3InputBarElement, DOMUtils } from "../../utils/ui-components"; -let t3DebounceTimeout: NodeJS.Timeout | null = null -let t3RouteObserver: MutationObserver | null = null -let t3UrlCheckInterval: NodeJS.Timeout | null = null -let t3ObserverThrottle: NodeJS.Timeout | null = null +let t3DebounceTimeout: NodeJS.Timeout | null = null; +let t3RouteObserver: MutationObserver | null = null; +let t3UrlCheckInterval: NodeJS.Timeout | null = null; +let t3ObserverThrottle: NodeJS.Timeout | null = null; export function initializeT3() { if (!DOMUtils.isOnDomain(DOMAINS.T3)) { - return + return; } if (document.body.hasAttribute("data-t3-initialized")) { - return + return; } setTimeout(() => { - console.log("Adding supermemory icon to T3 input") - addSupermemoryIconToT3Input() - setupT3AutoFetch() - }, 2000) + console.log("Adding supermemory icon to T3 input"); + addSupermemoryIconToT3Input(); + setupT3AutoFetch(); + }, 2000); - setupT3PromptCapture() + setupT3PromptCapture(); - setupT3RouteChangeDetection() + setupT3RouteChangeDetection(); - document.body.setAttribute("data-t3-initialized", "true") + document.body.setAttribute("data-t3-initialized", "true"); } function setupT3RouteChangeDetection() { if (t3RouteObserver) { - t3RouteObserver.disconnect() + t3RouteObserver.disconnect(); } if (t3UrlCheckInterval) { - clearInterval(t3UrlCheckInterval) + clearInterval(t3UrlCheckInterval); } if (t3ObserverThrottle) { - clearTimeout(t3ObserverThrottle) - t3ObserverThrottle = null + clearTimeout(t3ObserverThrottle); + t3ObserverThrottle = null; } - let currentUrl = window.location.href + let currentUrl = window.location.href; const checkForRouteChange = () => { if (window.location.href !== currentUrl) { - currentUrl = window.location.href - console.log("T3 route changed, re-adding supermemory icon") + currentUrl = window.location.href; + console.log("T3 route changed, re-adding supermemory icon"); setTimeout(() => { - addSupermemoryIconToT3Input() - setupT3AutoFetch() - }, 1000) + addSupermemoryIconToT3Input(); + setupT3AutoFetch(); + }, 1000); } - } + }; - t3UrlCheckInterval = setInterval(checkForRouteChange, 2000) + t3UrlCheckInterval = setInterval(checkForRouteChange, 2000); t3RouteObserver = new MutationObserver((mutations) => { if (t3ObserverThrottle) { - return + return; } - let shouldRecheck = false + let shouldRecheck = false; mutations.forEach((mutation) => { if (mutation.type === "childList" && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { - const element = node as Element + const element = node as Element; if ( element.querySelector?.("textarea") || element.querySelector?.('div[contenteditable="true"]') || element.matches?.("textarea") || element.matches?.('div[contenteditable="true"]') ) { - shouldRecheck = true + shouldRecheck = true; } } - }) + }); } - }) + }); if (shouldRecheck) { t3ObserverThrottle = setTimeout(() => { try { - t3ObserverThrottle = null - addSupermemoryIconToT3Input() - setupT3AutoFetch() + t3ObserverThrottle = null; + addSupermemoryIconToT3Input(); + setupT3AutoFetch(); } catch (error) { - console.error("Error in T3 observer callback:", error) + console.error("Error in T3 observer callback:", error); } - }, 300) + }, 300); } - }) + }); try { t3RouteObserver.observe(document.body, { childList: true, subtree: true, - }) + }); } catch (error) { - console.error("Failed to set up T3 route observer:", error) + console.error("Failed to set up T3 route observer:", error); if (t3UrlCheckInterval) { - clearInterval(t3UrlCheckInterval) + clearInterval(t3UrlCheckInterval); } - t3UrlCheckInterval = setInterval(checkForRouteChange, 1000) + t3UrlCheckInterval = setInterval(checkForRouteChange, 1000); } } function addSupermemoryIconToT3Input() { 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(POSTHOG_EVENT_KEY.T3_CHAT_MEMORIES_SEARCHED) - }) + await getRelatedMemoriesForT3( + POSTHOG_EVENT_KEY.T3_CHAT_MEMORIES_SEARCHED, + ); + }); - 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(actionSource: string) { try { - let userQuery = "" + let userQuery = ""; const supermemoryContainer = document.querySelector( '[data-supermemory-icon-added="true"]', - ) + ); if ( supermemoryContainer?.parentElement?.parentElement?.previousElementSibling ) { 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 for T3") - return + console.log("No query text found for T3"); + return; } - const icon = document.querySelector('[id*="sm-t3-input-bar-element"]') + const icon = document.querySelector('[id*="sm-t3-input-bar-element"]'); - const iconElement = icon as HTMLElement + const iconElement = icon as HTMLElement; if (!iconElement) { - console.warn("T3 icon element not found, cannot update feedback") - return + console.warn("T3 icon element not found, cannot update feedback"); + return; } - updateT3IconFeedback("Searching memories...", iconElement) + updateT3IconFeedback("Searching memories...", iconElement); const timeoutPromise = new Promise((_, reject) => setTimeout( () => reject(new Error("Memory search timeout")), UI_CONFIG.API_REQUEST_TIMEOUT, ), - ) + ); const response = await Promise.race([ browser.runtime.sendMessage({ @@ -211,15 +213,15 @@ async function getRelatedMemoriesForT3(actionSource: string) { actionSource: actionSource, }), timeoutPromise, - ]) + ]); - 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 @@ -227,46 +229,46 @@ async function getRelatedMemoriesForT3(actionSource: string) { 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") { - ;(textareaElement as HTMLTextAreaElement).dataset.supermemories = - `<br>Supermemories of user (only for the reference): ${response.data}</br>` + (textareaElement as HTMLTextAreaElement).dataset.supermemories = + `<br>Supermemories of user (only for the reference): ${response.data}</br>`; } else { - ;(textareaElement as HTMLElement).dataset.supermemories = - `<br>Supermemories of user (only for the reference): ${response.data}</br>` + (textareaElement as HTMLElement).dataset.supermemories = + `<br>Supermemories of user (only for the reference): ${response.data}</br>`; } - iconElement.dataset.memoriesData = response.data + iconElement.dataset.memoriesData = response.data; - updateT3IconFeedback("Included Memories", iconElement) + updateT3IconFeedback("Included Memories", iconElement); } else { - console.warn("T3 input area not found after successful memory fetch") - updateT3IconFeedback("Memories found", iconElement) + console.warn("T3 input area not found after successful memory fetch"); + updateT3IconFeedback("Memories found", iconElement); } } else { - console.warn("No memories found or API response invalid for T3") - updateT3IconFeedback("No memories found", iconElement) + console.warn("No memories found or API response invalid for T3"); + updateT3IconFeedback("No memories found", iconElement); } } catch (error) { - console.error("Error getting related memories for T3:", error) + console.error("Error getting related memories for T3:", error); try { const icon = document.querySelector( '[id*="sm-t3-input-bar-element"]', - ) as HTMLElement + ) as HTMLElement; if (icon) { - updateT3IconFeedback("Error fetching memories", icon) + updateT3IconFeedback("Error fetching memories", icon); } } catch (feedbackError) { - console.error("Failed to update T3 error feedback:", feedbackError) + console.error("Failed to update T3 error feedback:", feedbackError); } } } @@ -277,10 +279,10 @@ function updateT3IconFeedback( resetAfter = 0, ) { if (!iconElement.dataset.originalHtml) { - iconElement.dataset.originalHtml = iconElement.innerHTML + iconElement.dataset.originalHtml = iconElement.innerHTML; } - const feedbackDiv = document.createElement("div") + const feedbackDiv = document.createElement("div"); feedbackDiv.style.cssText = ` display: flex; align-items: center; @@ -293,15 +295,15 @@ function updateT3IconFeedback( font-weight: 500; cursor: ${message === "Included Memories" ? "pointer" : "default"}; position: relative; - ` + `; feedbackDiv.innerHTML = ` <span>✓</span> <span>${message}</span> - ` + `; if (message === "Included Memories" && iconElement.dataset.memoriesData) { - const popup = document.createElement("div") + const popup = document.createElement("div"); popup.style.cssText = ` position: fixed; bottom: 80px; @@ -319,9 +321,9 @@ function updateT3IconFeedback( z-index: 999999; display: none; border: 1px solid #333; - ` + `; - const header = document.createElement("div") + const header = document.createElement("div"); header.style.cssText = ` display: flex; justify-content: space-between; @@ -329,28 +331,28 @@ function updateT3IconFeedback( padding: 8px; border-bottom: 1px solid #333; opacity: 0.8; - ` + `; header.innerHTML = ` <span style="font-weight: 600; color: #fff;">Included Memories</span> - ` + `; - const content = document.createElement("div") + const content = document.createElement("div"); content.style.cssText = ` padding: 0; max-height: 300px; overflow-y: auto; - ` + `; - const memoriesText = iconElement.dataset.memoriesData || "" - console.log("Memories text:", memoriesText) + const memoriesText = iconElement.dataset.memoriesData || ""; + console.log("Memories text:", memoriesText); const individualMemories = memoriesText .split(/[,\n]/) .map((memory) => memory.trim()) - .filter((memory) => memory.length > 0 && memory !== ",") - console.log("Individual memories:", individualMemories) + .filter((memory) => memory.length > 0 && memory !== ","); + console.log("Individual memories:", individualMemories); individualMemories.forEach((memory, index) => { - const memoryItem = document.createElement("div") + const memoryItem = document.createElement("div"); memoryItem.style.cssText = ` display: flex; align-items: center; @@ -358,16 +360,16 @@ function updateT3IconFeedback( padding: 10px; font-size: 13px; line-height: 1.4; - ` + `; - const memoryText = document.createElement("div") + const memoryText = document.createElement("div"); memoryText.style.cssText = ` flex: 1; color: #e5e5e5; - ` - memoryText.textContent = memory.trim() + `; + memoryText.textContent = memory.trim(); - const removeBtn = document.createElement("button") + const removeBtn = document.createElement("button"); removeBtn.style.cssText = ` background: transparent; color: #9ca3af; @@ -380,164 +382,169 @@ function updateT3IconFeedback( display: flex; align-items: center; justify-content: center; - ` - removeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>` - removeBtn.dataset.memoryIndex = index.toString() + `; + removeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`; + removeBtn.dataset.memoryIndex = index.toString(); removeBtn.addEventListener("mouseenter", () => { - removeBtn.style.color = "#ef4444" - }) + removeBtn.style.color = "#ef4444"; + }); removeBtn.addEventListener("mouseleave", () => { - removeBtn.style.color = "#9ca3af" - }) + removeBtn.style.color = "#9ca3af"; + }); - memoryItem.appendChild(memoryText) - memoryItem.appendChild(removeBtn) - content.appendChild(memoryItem) - }) + memoryItem.appendChild(memoryText); + memoryItem.appendChild(removeBtn); + content.appendChild(memoryItem); + }); - popup.appendChild(header) - popup.appendChild(content) - document.body.appendChild(popup) + popup.appendChild(header); + popup.appendChild(content); + document.body.appendChild(popup); feedbackDiv.addEventListener("mouseenter", () => { - const textSpan = feedbackDiv.querySelector("span:last-child") + const textSpan = feedbackDiv.querySelector("span:last-child"); if (textSpan) { - textSpan.textContent = "Click to see memories" + textSpan.textContent = "Click to see memories"; } - }) + }); feedbackDiv.addEventListener("mouseleave", () => { - const textSpan = feedbackDiv.querySelector("span:last-child") + const textSpan = feedbackDiv.querySelector("span:last-child"); if (textSpan) { - textSpan.textContent = "Included Memories" + textSpan.textContent = "Included Memories"; } - }) + }); feedbackDiv.addEventListener("click", (e) => { - e.stopPropagation() - popup.style.display = "block" - }) + e.stopPropagation(); + popup.style.display = "block"; + }); document.addEventListener("click", (e) => { if (!popup.contains(e.target as Node)) { - popup.style.display = "none" + popup.style.display = "none"; } - }) + }); content.querySelectorAll("button[data-memory-index]").forEach((button) => { - const htmlButton = button as HTMLButtonElement + const htmlButton = button as HTMLButtonElement; htmlButton.addEventListener("click", () => { - const index = Number.parseInt(htmlButton.dataset.memoryIndex || "0", 10) - const memoryItem = htmlButton.parentElement + const index = Number.parseInt( + htmlButton.dataset.memoryIndex || "0", + 10, + ); + const memoryItem = htmlButton.parentElement; if (memoryItem) { - content.removeChild(memoryItem) + content.removeChild(memoryItem); } const currentMemories = (iconElement.dataset.memoriesData || "") .split(/[,\n]/) .map((memory) => memory.trim()) - .filter((memory) => memory.length > 0 && memory !== ",") - currentMemories.splice(index, 1) + .filter((memory) => memory.length > 0 && memory !== ","); + currentMemories.splice(index, 1); - const updatedMemories = currentMemories.join(" ,") + const updatedMemories = currentMemories.join(" ,"); - iconElement.dataset.memoriesData = updatedMemories + iconElement.dataset.memoriesData = updatedMemories; const textareaElement = (document.querySelector("textarea") as HTMLTextAreaElement) || - (document.querySelector('div[contenteditable="true"]') as HTMLElement) + (document.querySelector( + 'div[contenteditable="true"]', + ) as HTMLElement); if (textareaElement) { - textareaElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${updatedMemories}</div>` + textareaElement.dataset.supermemories = `<div>Supermemories of user (only for the reference): ${updatedMemories}</div>`; } content .querySelectorAll("button[data-memory-index]") .forEach((btn, newIndex) => { - const htmlBtn = btn as HTMLButtonElement - htmlBtn.dataset.memoryIndex = newIndex.toString() - }) + const htmlBtn = btn as HTMLButtonElement; + htmlBtn.dataset.memoryIndex = newIndex.toString(); + }); if (currentMemories.length <= 1) { if (textareaElement?.dataset.supermemories) { - delete textareaElement.dataset.supermemories - delete iconElement.dataset.memoriesData - iconElement.innerHTML = iconElement.dataset.originalHtml || "" - delete iconElement.dataset.originalHtml + delete textareaElement.dataset.supermemories; + delete iconElement.dataset.memoriesData; + iconElement.innerHTML = iconElement.dataset.originalHtml || ""; + delete iconElement.dataset.originalHtml; } - popup.style.display = "none" + popup.style.display = "none"; if (document.body.contains(popup)) { - document.body.removeChild(popup) + document.body.removeChild(popup); } } - }) - }) + }); + }); setTimeout(() => { if (document.body.contains(popup)) { - document.body.removeChild(popup) + document.body.removeChild(popup); } - }, 300000) + }, 300000); } - iconElement.innerHTML = "" - iconElement.appendChild(feedbackDiv) + iconElement.innerHTML = ""; + iconElement.appendChild(feedbackDiv); if (resetAfter > 0) { setTimeout(() => { - iconElement.innerHTML = iconElement.dataset.originalHtml || "" - delete iconElement.dataset.originalHtml - }, resetAfter) + iconElement.innerHTML = iconElement.dataset.originalHtml || ""; + delete iconElement.dataset.originalHtml; + }, resetAfter); } } function setupT3PromptCapture() { if (document.body.hasAttribute("data-t3-prompt-capture-setup")) { - return + return; } - document.body.setAttribute("data-t3-prompt-capture-setup", "true") + document.body.setAttribute("data-t3-prompt-capture-setup", "true"); const captureT3PromptContent = async (source: string) => { - let promptContent = "" + let promptContent = ""; - const textarea = document.querySelector("textarea") as HTMLTextAreaElement + const textarea = document.querySelector("textarea") as HTMLTextAreaElement; if (textarea) { - promptContent = textarea.value || "" + promptContent = textarea.value || ""; } if (!promptContent) { const contentEditableDiv = document.querySelector( 'div[contenteditable="true"]', - ) as HTMLElement + ) as HTMLElement; if (contentEditableDiv) { promptContent = - contentEditableDiv.textContent || contentEditableDiv.innerText || "" + contentEditableDiv.textContent || contentEditableDiv.innerText || ""; } } const textareaElement = textarea || - (document.querySelector('div[contenteditable="true"]') as HTMLElement) - const storedMemories = textareaElement?.dataset.supermemories + (document.querySelector('div[contenteditable="true"]') as HTMLElement); + const storedMemories = textareaElement?.dataset.supermemories; if ( storedMemories && textareaElement && !promptContent.includes("Supermemories of user") ) { if (textareaElement.tagName === "TEXTAREA") { - ;(textareaElement as HTMLTextAreaElement).value = - `${promptContent} ${storedMemories}` - promptContent = (textareaElement as HTMLTextAreaElement).value + (textareaElement as HTMLTextAreaElement).value = + `${promptContent} ${storedMemories}`; + promptContent = (textareaElement as HTMLTextAreaElement).value; } else { - textareaElement.innerHTML = `${textareaElement.innerHTML} ${storedMemories}` + textareaElement.innerHTML = `${textareaElement.innerHTML} ${storedMemories}`; promptContent = - textareaElement.textContent || textareaElement.innerText || "" + textareaElement.textContent || textareaElement.innerText || ""; } } if (promptContent.trim()) { - console.log(`T3 prompt submitted via ${source}:`, promptContent) + console.log(`T3 prompt submitted via ${source}:`, promptContent); try { await browser.runtime.sendMessage({ @@ -547,48 +554,48 @@ function setupT3PromptCapture() { platform: "t3", source: source, }, - }) + }); } catch (error) { - console.error("Error sending T3 prompt to background:", error) + console.error("Error sending T3 prompt to background:", error); } } - const icons = document.querySelectorAll('[id*="sm-t3-input-bar-element"]') + const icons = document.querySelectorAll('[id*="sm-t3-input-bar-element"]'); icons.forEach((icon) => { - const iconElement = icon as HTMLElement + const iconElement = icon as HTMLElement; if (iconElement.dataset.originalHtml) { - iconElement.innerHTML = iconElement.dataset.originalHtml - delete iconElement.dataset.originalHtml - delete iconElement.dataset.memoriesData + iconElement.innerHTML = iconElement.dataset.originalHtml; + delete iconElement.dataset.originalHtml; + delete iconElement.dataset.memoriesData; } - }) + }); if (textareaElement?.dataset.supermemories) { - delete textareaElement.dataset.supermemories + delete textareaElement.dataset.supermemories; } - } + }; document.addEventListener( "click", async (event) => { - const target = event.target as HTMLElement + const target = event.target as HTMLElement; const sendButton = target.closest("button.focus-visible\\:ring-ring") || target.closest('button[class*="bg-[rgb(162,59,103)]"]') || - target.closest('button[class*="rounded-lg"]') + target.closest('button[class*="rounded-lg"]'); if (sendButton) { - await captureT3PromptContent("button click") + await captureT3PromptContent("button click"); } }, true, - ) + ); document.addEventListener( "keydown", async (event) => { - const target = event.target as HTMLElement + const target = event.target as HTMLElement; if ( (target.matches("textarea") || @@ -597,9 +604,9 @@ function setupT3PromptCapture() { !event.shiftKey ) { if (target.matches("textarea")) { - const promptContent = (target as HTMLTextAreaElement).value || "" + const promptContent = (target as HTMLTextAreaElement).value || ""; if (promptContent.trim()) { - console.log("T3 prompt submitted via Enter key:", promptContent) + console.log("T3 prompt submitted via Enter key:", promptContent); try { await browser.runtime.sendMessage({ @@ -610,83 +617,83 @@ function setupT3PromptCapture() { source: "Enter key", }, actionSource: "t3", - }) + }); } catch (error) { console.error( "Error sending T3 textarea prompt to background:", error, - ) + ); } } } else { - await captureT3PromptContent("Enter key") + await captureT3PromptContent("Enter key"); } } }, true, - ) + ); } async function setupT3AutoFetch() { const result = await chrome.storage.local.get([ STORAGE_KEYS.AUTO_SEARCH_ENABLED, - ]) - const autoSearchEnabled = result[STORAGE_KEYS.AUTO_SEARCH_ENABLED] ?? false + ]); + const autoSearchEnabled = result[STORAGE_KEYS.AUTO_SEARCH_ENABLED] ?? false; if (!autoSearchEnabled) { - return + return; } const textareaElement = (document.querySelector("textarea") as HTMLTextAreaElement) || - (document.querySelector('div[contenteditable="true"]') as HTMLElement) + (document.querySelector('div[contenteditable="true"]') as HTMLElement); if ( !textareaElement || textareaElement.hasAttribute("data-supermemory-auto-fetch") ) { - return + return; } - textareaElement.setAttribute("data-supermemory-auto-fetch", "true") + textareaElement.setAttribute("data-supermemory-auto-fetch", "true"); const handleInput = () => { if (t3DebounceTimeout) { - clearTimeout(t3DebounceTimeout) + clearTimeout(t3DebounceTimeout); } t3DebounceTimeout = setTimeout(async () => { - let content = "" + let content = ""; if (textareaElement.tagName === "TEXTAREA") { - content = (textareaElement as HTMLTextAreaElement).value?.trim() || "" + content = (textareaElement as HTMLTextAreaElement).value?.trim() || ""; } else { - content = textareaElement.textContent?.trim() || "" + content = textareaElement.textContent?.trim() || ""; } if (content.length > 2) { await getRelatedMemoriesForT3( POSTHOG_EVENT_KEY.T3_CHAT_MEMORIES_AUTO_SEARCHED, - ) + ); } else if (content.length === 0) { const icons = document.querySelectorAll( '[id*="sm-t3-input-bar-element"]', - ) + ); icons.forEach((icon) => { - const iconElement = icon as HTMLElement + const iconElement = icon as HTMLElement; if (iconElement.dataset.originalHtml) { - iconElement.innerHTML = iconElement.dataset.originalHtml - delete iconElement.dataset.originalHtml - delete iconElement.dataset.memoriesData + iconElement.innerHTML = iconElement.dataset.originalHtml; + delete iconElement.dataset.originalHtml; + delete iconElement.dataset.memoriesData; } - }) + }); if (textareaElement.dataset.supermemories) { - delete textareaElement.dataset.supermemories + delete textareaElement.dataset.supermemories; } } - }, UI_CONFIG.AUTO_SEARCH_DEBOUNCE_DELAY) - } + }, UI_CONFIG.AUTO_SEARCH_DEBOUNCE_DELAY); + }; - textareaElement.addEventListener("input", handleInput) + textareaElement.addEventListener("input", handleInput); } diff --git a/apps/browser-extension/entrypoints/content/twitter.ts b/apps/browser-extension/entrypoints/content/twitter.ts index 15c6ec50..272983cf 100644 --- a/apps/browser-extension/entrypoints/content/twitter.ts +++ b/apps/browser-extension/entrypoints/content/twitter.ts @@ -3,24 +3,24 @@ import { ELEMENT_IDS, MESSAGE_TYPES, POSTHOG_EVENT_KEY, -} from "../../utils/constants" -import { trackEvent } from "../../utils/posthog" -import { createTwitterImportButton, DOMUtils } from "../../utils/ui-components" +} from "../../utils/constants"; +import { trackEvent } from "../../utils/posthog"; +import { createTwitterImportButton, DOMUtils } from "../../utils/ui-components"; export function initializeTwitter() { if (!DOMUtils.isOnDomain(DOMAINS.TWITTER)) { - return + return; } // Initial setup if (window.location.pathname === "/i/bookmarks") { setTimeout(() => { - addTwitterImportButton() - }, 2000) + addTwitterImportButton(); + }, 2000); } else { // Remove button if not on bookmarks page if (DOMUtils.elementExists(ELEMENT_IDS.TWITTER_IMPORT_BUTTON)) { - DOMUtils.removeElement(ELEMENT_IDS.TWITTER_IMPORT_BUTTON) + DOMUtils.removeElement(ELEMENT_IDS.TWITTER_IMPORT_BUTTON); } } } @@ -28,75 +28,75 @@ export function initializeTwitter() { function addTwitterImportButton() { // 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 + return; } 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) + console.error("Error starting import:", error); } - }) + }); - document.body.appendChild(button) + document.body.appendChild(button); } export function updateTwitterImportUI(message: { - type: string - importedMessage?: string - totalImported?: number + type: string; + importedMessage?: string; + totalImported?: number; }) { const importButton = document.getElementById( ELEMENT_IDS.TWITTER_IMPORT_BUTTON, - ) - if (!importButton) return + ); + if (!importButton) return; - const iconUrl = browser.runtime.getURL("/icon-16.png") + const iconUrl = browser.runtime.getURL("/icon-16.png"); if (message.type === MESSAGE_TYPES.IMPORT_UPDATE) { 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" + `; + importButton.style.cursor = "default"; } if (message.type === MESSAGE_TYPES.IMPORT_DONE) { 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(() => { 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) + `; + importButton.style.cursor = "pointer"; + }, 3000); } } export function handleTwitterNavigation() { if (!DOMUtils.isOnDomain(DOMAINS.TWITTER)) { - return + return; } if (window.location.pathname === "/i/bookmarks") { - addTwitterImportButton() + addTwitterImportButton(); } else { if (DOMUtils.elementExists(ELEMENT_IDS.TWITTER_IMPORT_BUTTON)) { - DOMUtils.removeElement(ELEMENT_IDS.TWITTER_IMPORT_BUTTON) + DOMUtils.removeElement(ELEMENT_IDS.TWITTER_IMPORT_BUTTON); } } -}
\ No newline at end of file +} diff --git a/apps/browser-extension/entrypoints/popup/App.tsx b/apps/browser-extension/entrypoints/popup/App.tsx index 3e4f15e2..3d4aeedd 100644 --- a/apps/browser-extension/entrypoints/popup/App.tsx +++ b/apps/browser-extension/entrypoints/popup/App.tsx @@ -1,34 +1,35 @@ -import { useQueryClient } from "@tanstack/react-query" -import { useEffect, useState } from "react" -import "./App.css" -import { MESSAGE_TYPES, 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 [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" | "settings">( "save", - ) - const [autoSearchEnabled, setAutoSearchEnabled] = useState<boolean>(false) + ); + const [autoSearchEnabled, setAutoSearchEnabled] = useState<boolean>(false); - 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 () => { @@ -36,131 +37,131 @@ function App() { const result = await chrome.storage.local.get([ STORAGE_KEYS.BEARER_TOKEN, STORAGE_KEYS.AUTO_SEARCH_ENABLED, - ]) - const isSignedIn = !!result[STORAGE_KEYS.BEARER_TOKEN] - setUserSignedIn(isSignedIn) + ]); + const isSignedIn = !!result[STORAGE_KEYS.BEARER_TOKEN]; + setUserSignedIn(isSignedIn); const autoSearchSetting = - result[STORAGE_KEYS.AUTO_SEARCH_ENABLED] ?? false - setAutoSearchEnabled(autoSearchSetting) + result[STORAGE_KEYS.AUTO_SEARCH_ENABLED] ?? false; + setAutoSearchEnabled(autoSearchSetting); } 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) { 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", - }) + }); } - window.close() + 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) + console.error("Failed to show error toast:", toastError); } - window.close() + window.close(); } finally { - setSaving(false) + setSaving(false); } - } + }; const handleAutoSearchToggle = async (enabled: boolean) => { try { await chrome.storage.local.set({ [STORAGE_KEYS.AUTO_SEARCH_ENABLED]: enabled, - }) - setAutoSearchEnabled(enabled) + }); + setAutoSearchEnabled(enabled); } catch (error) { - console.error("Error updating auto search setting:", error) + console.error("Error updating auto search setting:", error); } - } + }; const handleSignOut = async () => { try { - 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() + 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 ( @@ -179,7 +180,7 @@ function App() { <div>Loading...</div> </div> </div> - ) + ); } return ( @@ -330,7 +331,7 @@ function App() { onClick={() => { chrome.tabs.create({ url: "https://chatgpt.com/#settings/Personalization", - }) + }); }} type="button" > @@ -361,17 +362,17 @@ function App() { const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true, - }) + }); - const targetUrl = "https://x.com/i/bookmarks" + const targetUrl = "https://x.com/i/bookmarks"; if (activeTab?.url === targetUrl) { - return + return; } await chrome.tabs.create({ url: targetUrl, - }) + }); }} type="button" > @@ -504,7 +505,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" > @@ -519,7 +520,7 @@ function App() { url: import.meta.env.PROD ? "https://app.supermemory.ai/login" : "http://localhost:3000/login", - }) + }); }} type="button" > @@ -530,7 +531,7 @@ function App() { )} </div> </div> - ) + ); } -export default App +export default App; diff --git a/apps/browser-extension/entrypoints/popup/main.tsx b/apps/browser-extension/entrypoints/popup/main.tsx index 746e1018..5376a552 100644 --- a/apps/browser-extension/entrypoints/popup/main.tsx +++ b/apps/browser-extension/entrypoints/popup/main.tsx @@ -1,11 +1,11 @@ -import { QueryClientProvider } from "@tanstack/react-query" -import React from "react" -import ReactDOM from "react-dom/client" -import { queryClient } from "../../utils/query-client" -import App from "./App.js" -import "./style.css" +import { QueryClientProvider } from "@tanstack/react-query"; +import React from "react"; +import ReactDOM from "react-dom/client"; +import { queryClient } from "../../utils/query-client"; +import App from "./App.js"; +import "./style.css"; -const rootElement = document.getElementById("root") +const rootElement = document.getElementById("root"); if (rootElement) { ReactDOM.createRoot(rootElement).render( <React.StrictMode> @@ -13,5 +13,5 @@ if (rootElement) { <App /> </QueryClientProvider> </React.StrictMode>, - ) + ); } diff --git a/apps/browser-extension/entrypoints/welcome/Welcome.tsx b/apps/browser-extension/entrypoints/welcome/Welcome.tsx index 9463eba4..00bd01cc 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; diff --git a/apps/browser-extension/entrypoints/welcome/main.tsx b/apps/browser-extension/entrypoints/welcome/main.tsx index 2df8dbf1..84ad6b8c 100644 --- a/apps/browser-extension/entrypoints/welcome/main.tsx +++ b/apps/browser-extension/entrypoints/welcome/main.tsx @@ -1,11 +1,11 @@ -import { QueryClientProvider } from "@tanstack/react-query" -import React from "react" -import ReactDOM from "react-dom/client" -import { queryClient } from "../../utils/query-client" -import Welcome from "./Welcome" -import "./welcome.css" +import { QueryClientProvider } from "@tanstack/react-query"; +import React from "react"; +import ReactDOM from "react-dom/client"; +import { queryClient } from "../../utils/query-client"; +import Welcome from "./Welcome"; +import "./welcome.css"; -const rootElement = document.getElementById("root") +const rootElement = document.getElementById("root"); if (rootElement) { ReactDOM.createRoot(rootElement).render( <React.StrictMode> @@ -13,5 +13,5 @@ if (rootElement) { <Welcome /> </QueryClientProvider> </React.StrictMode>, - ) + ); } |