diff --git a/apps/browser-extension/entrypoints/content.ts b/apps/browser-extension/entrypoints/content.ts
index 97f5fc44..a66532f5 100644
--- a/apps/browser-extension/entrypoints/content.ts
+++ b/apps/browser-extension/entrypoints/content.ts
@@ -6,6 +6,8 @@ import {
} from "../utils/constants"
import {
createChatGPTInputBarElement,
+ createClaudeInputBarElement,
+ createT3InputBarElement,
createSaveTweetElement,
createTwitterImportButton,
createTwitterImportUI,
@@ -36,6 +38,12 @@ export default defineContentScript({
addSupermemoryButtonToMemoriesDialog()
addSaveChatGPTElementBeforeComposerBtn()
}
+ if (DOMUtils.isOnDomain(DOMAINS.CLAUDE)) {
+ addSupermemoryIconToClaudeInput()
+ }
+ if (DOMUtils.isOnDomain(DOMAINS.T3)) {
+ addSupermemoryIconToT3Input()
+ }
if (DOMUtils.isOnDomain(DOMAINS.TWITTER)) {
addTwitterImportButton()
//addSaveTweetElement();
@@ -46,21 +54,6 @@ export default defineContentScript({
childList: true,
subtree: true,
})
-
- if (
- window.location.hostname === "chatgpt.com" ||
- window.location.hostname === "chat.openai.com"
- ) {
- addSupermemoryButtonToMemoriesDialog()
- addSaveChatGPTElementBeforeComposerBtn()
- }
- if (
- window.location.hostname === "x.com" ||
- window.location.hostname === "twitter.com"
- ) {
- addTwitterImportButton()
- //addSaveTweetElement();
- }
}
if (DOMUtils.isOnDomain(DOMAINS.TWITTER)) {
@@ -70,6 +63,18 @@ export default defineContentScript({
}, 2000)
}
+ if (DOMUtils.isOnDomain(DOMAINS.CLAUDE)) {
+ setTimeout(() => {
+ addSupermemoryIconToClaudeInput() // Wait 2 seconds for Claude page to load
+ }, 2000)
+ }
+
+ if (DOMUtils.isOnDomain(DOMAINS.T3)) {
+ setTimeout(() => {
+ addSupermemoryIconToT3Input() // Wait 2 seconds for T3 page to load
+ }, 2000)
+ }
+
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", observeForMemoriesDialog)
} else {
@@ -390,6 +395,206 @@ export default defineContentScript({
})
}
+ function addSupermemoryIconToClaudeInput() {
+ if (!DOMUtils.isOnDomain(DOMAINS.CLAUDE)) {
+ return
+ }
+
+ const targetContainers = document.querySelectorAll(
+ ".relative.flex-1.flex.items-center.gap-2.shrink.min-w-0",
+ )
+
+ targetContainers.forEach((container) => {
+ if (container.hasAttribute("data-supermemory-icon-added")) {
+ return
+ }
+
+ const existingIcon = container.querySelector(
+ `#${ELEMENT_IDS.CLAUDE_INPUT_BAR_ELEMENT}`,
+ )
+ if (existingIcon) {
+ container.setAttribute("data-supermemory-icon-added", "true")
+ return
+ }
+
+ const supermemoryIcon = createClaudeInputBarElement(async () => {
+ await getRelatedMemoriesForClaude()
+ })
+
+ 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.insertBefore(supermemoryIcon, container.firstChild)
+ })
+ }
+
+ async function getRelatedMemoriesForClaude() {
+ try {
+ 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 || ""
+ }
+
+ if (!userQuery.trim()) {
+ const textareaElement = document.querySelector('div[contenteditable="true"]') as HTMLElement
+ userQuery = 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
+ if (text?.trim()) {
+ userQuery = text.trim()
+ break
+ }
+ }
+ }
+
+ console.log("Claude query extracted:", userQuery)
+
+ if (!userQuery.trim()) {
+ console.log("No query text found")
+ DOMUtils.showToast("error")
+ return
+ }
+
+ const response = await browser.runtime.sendMessage({
+ action: MESSAGE_TYPES.GET_RELATED_MEMORIES,
+ data: userQuery,
+ })
+
+ console.log("Claude memories response:", response)
+
+ if (response.success && response.data) {
+ const textareaElement = document.querySelector('div[contenteditable="true"]') as HTMLElement
+
+ if (textareaElement) {
+ const currentContent = textareaElement.innerHTML
+ textareaElement.innerHTML = `${currentContent}
Supermemories: ${response.data}`
+
+ textareaElement.dispatchEvent(new Event('input', { bubbles: true }))
+ } else {
+ console.log("Could not find Claude input area")
+ }
+ } else {
+ console.log("Failed to get memories:", response.error || "Unknown error")
+ }
+ } catch (error) {
+ console.error("Error getting related memories for Claude:", error)
+ }
+ }
+
+ function addSupermemoryIconToT3Input() {
+ if (!DOMUtils.isOnDomain(DOMAINS.T3)) {
+ return
+ }
+
+ const targetContainers = document.querySelectorAll(
+ ".flex.min-w-0.items-center.gap-2.overflow-hidden",
+ )
+
+ targetContainers.forEach((container) => {
+ if (container.hasAttribute("data-supermemory-icon-added")) {
+ return
+ }
+
+ const existingIcon = container.querySelector(
+ `#${ELEMENT_IDS.T3_INPUT_BAR_ELEMENT}`,
+ )
+ if (existingIcon) {
+ container.setAttribute("data-supermemory-icon-added", "true")
+ return
+ }
+
+ const supermemoryIcon = createT3InputBarElement(async () => {
+ await getRelatedMemoriesForT3()
+ })
+
+ 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.insertBefore(supermemoryIcon, container.firstChild)
+ })
+ }
+
+ async function getRelatedMemoriesForT3() {
+ try {
+ 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 || ""
+ }
+
+ if (!userQuery.trim()) {
+ const textareaElement = document.querySelector('div[contenteditable="true"]') as HTMLElement
+ userQuery = textareaElement?.innerText || textareaElement?.textContent || ""
+ }
+
+ if (!userQuery.trim()) {
+ const textareas = document.querySelectorAll('textarea')
+ for (const textarea of textareas) {
+ const text = (textarea as HTMLTextAreaElement).value
+ if (text?.trim()) {
+ userQuery = text.trim()
+ break
+ }
+ }
+ }
+
+ console.log("T3 query extracted:", userQuery)
+
+ if (!userQuery.trim()) {
+ console.log("No query text found")
+ return
+ }
+
+ const response = await browser.runtime.sendMessage({
+ action: MESSAGE_TYPES.GET_RELATED_MEMORIES,
+ data: userQuery,
+ })
+
+ console.log("T3 memories response:", response)
+
+ if (response.success && response.data) {
+ let textareaElement = null
+ const supermemoryContainer = document.querySelector('[data-supermemory-icon-added="true"]')
+ if (supermemoryContainer?.parentElement?.parentElement?.previousElementSibling) {
+ textareaElement = supermemoryContainer.parentElement.parentElement.previousElementSibling.querySelector('textarea')
+ }
+
+ if (!textareaElement) {
+ textareaElement = document.querySelector('div[contenteditable="true"]') as HTMLElement
+ }
+
+ if (textareaElement) {
+ if (textareaElement.tagName === 'TEXTAREA') {
+ const currentContent = (textareaElement as HTMLTextAreaElement).value
+ ;(textareaElement as HTMLTextAreaElement).value = `${currentContent}\n\nSupermemories: ${response.data}`
+ } else {
+ const currentContent = textareaElement.innerHTML
+ textareaElement.innerHTML = `${currentContent}
Supermemories: ${response.data}`
+ }
+
+ textareaElement.dispatchEvent(new Event('input', { bubbles: true }))
+ } else {
+ console.log("Could not find T3 input area")
+ }
+ } else {
+ console.log("Failed to get memories:", response.error || "Unknown error")
+ }
+ } catch (error) {
+ console.error("Error getting related memories for T3:", error)
+ }
+ }
+
// TODO: Add Tweet Capture Functionality
function _addSaveTweetElement() {
if (!DOMUtils.isOnDomain(DOMAINS.TWITTER)) {
diff --git a/apps/browser-extension/utils/constants.ts b/apps/browser-extension/utils/constants.ts
index b499a359..7634759b 100644
--- a/apps/browser-extension/utils/constants.ts
+++ b/apps/browser-extension/utils/constants.ts
@@ -35,6 +35,8 @@ export const ELEMENT_IDS = {
SUPERMEMORY_SAVE_BUTTON: "sm-save-button",
SAVE_TWEET_ELEMENT: "sm-save-tweet-element",
CHATGPT_INPUT_BAR_ELEMENT: "sm-chatgpt-input-bar-element",
+ CLAUDE_INPUT_BAR_ELEMENT: "sm-claude-input-bar-element",
+ T3_INPUT_BAR_ELEMENT: "sm-t3-input-bar-element",
} as const
/**
@@ -53,6 +55,8 @@ export const UI_CONFIG = {
export const DOMAINS = {
TWITTER: ["x.com", "twitter.com"],
CHATGPT: ["chatgpt.com", "chat.openai.com"],
+ CLAUDE: ["claude.ai"],
+ T3: ["t3.chat"],
SUPERMEMORY: ["localhost", "supermemory.ai", "app.supermemory.ai"],
} as const
diff --git a/apps/browser-extension/utils/ui-components.ts b/apps/browser-extension/utils/ui-components.ts
index c160d63e..9c060017 100644
--- a/apps/browser-extension/utils/ui-components.ts
+++ b/apps/browser-extension/utils/ui-components.ts
@@ -369,6 +369,96 @@ export function createChatGPTInputBarElement(onClick: () => void): HTMLElement {
return iconButton
}
+/**
+ * Creates a save element button for Claude input bar
+ * @param onClick - Click handler for the button
+ * @returns HTMLElement - The save button element
+ */
+export function createClaudeInputBarElement(onClick: () => void): HTMLElement {
+ const iconButton = document.createElement("div")
+ iconButton.style.cssText = `
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ border-radius: 6px;
+ background: transparent;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ `
+
+ const iconFileName = "/icon-16.png"
+ const iconUrl = browser.runtime.getURL(iconFileName)
+ iconButton.innerHTML = `
+
+ `
+
+ iconButton.addEventListener("mouseenter", () => {
+ iconButton.style.backgroundColor = "rgba(0, 0, 0, 0.05)"
+ iconButton.style.borderColor = "rgba(0, 0, 0, 0.2)"
+ })
+
+ iconButton.addEventListener("mouseleave", () => {
+ iconButton.style.backgroundColor = "transparent"
+ iconButton.style.borderColor = "rgba(0, 0, 0, 0.1)"
+ })
+
+ iconButton.addEventListener("click", (event) => {
+ event.stopPropagation()
+ event.preventDefault()
+ onClick()
+ })
+
+ return iconButton
+}
+
+/**
+ * Creates a save element button for T3.chat input bar
+ * @param onClick - Click handler for the button
+ * @returns HTMLElement - The save button element
+ */
+export function createT3InputBarElement(onClick: () => void): HTMLElement {
+ const iconButton = document.createElement("div")
+ iconButton.style.cssText = `
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ border-radius: 6px;
+ background: transparent;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ `
+
+ const iconFileName = "/icon-16.png"
+ const iconUrl = browser.runtime.getURL(iconFileName)
+ iconButton.innerHTML = `
+
+ `
+
+ iconButton.addEventListener("mouseenter", () => {
+ iconButton.style.backgroundColor = "rgba(0, 0, 0, 0.05)"
+ iconButton.style.borderColor = "rgba(0, 0, 0, 0.2)"
+ })
+
+ iconButton.addEventListener("mouseleave", () => {
+ iconButton.style.backgroundColor = "transparent"
+ iconButton.style.borderColor = "rgba(0, 0, 0, 0.1)"
+ })
+
+ iconButton.addEventListener("click", (event) => {
+ event.stopPropagation()
+ event.preventDefault()
+ onClick()
+ })
+
+ return iconButton
+}
+
/**
* Utility functions for DOM manipulation
*/