diff --git a/apps/browser-extension/entrypoints/background.ts b/apps/browser-extension/entrypoints/background.ts index f9f9c979..52e05754 100644 --- a/apps/browser-extension/entrypoints/background.ts +++ b/apps/browser-extension/entrypoints/background.ts @@ -21,6 +21,45 @@ import type { MemoryPayload, } from "../utils/types" +const PLATFORM_LABELS: Record = { + chatgpt: "ChatGPT", + claude: "Claude", + gemini: "Gemini", + t3: "T3 Chat", + twitter: "X / Twitter", +} + +function normalizePlatform(value?: string): string | undefined { + if (!value) return undefined + return value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") +} + +function inferPlatformFromActionSource(actionSource: string): string | undefined { + const source = actionSource.toLowerCase() + if (source.includes("chatgpt")) return "chatgpt" + if (source.includes("claude")) return "claude" + if (source.includes("gemini")) return "gemini" + if (source.includes("t3")) return "t3" + if (source.includes("twitter") || source.includes("x_")) return "twitter" + return undefined +} + +function inferPlatformFromUrl(url?: string): string | undefined { + if (!url) return undefined + try { + const hostname = new URL(url).hostname + if (hostname === "chatgpt.com" || hostname === "chat.openai.com") { + return "chatgpt" + } + if (hostname === "claude.ai") return "claude" + if (hostname === "gemini.google.com") return "gemini" + if (hostname === "t3.chat") return "t3" + if (hostname === "x.com" || hostname === "twitter.com") return "twitter" + } catch { + return undefined + } +} + export default defineBackground(() => { let twitterImporter: TwitterImporter | null = null @@ -107,11 +146,32 @@ export default defineBackground(() => { content = data?.url || "" } + const platform = normalizePlatform(data.sourcePlatform) + || inferPlatformFromUrl(data.url) + || inferPlatformFromActionSource(actionSource) + const platformLabel = platform + ? data.sourcePlatformLabel || PLATFORM_LABELS[platform] || platform + : undefined + const metadata: MemoryPayload["metadata"] = { sm_source: "consumer", + sm_origin: "browser_extension", + sm_origin_action: actionSource, website_url: data.url, } + if (platform) { + metadata.sm_origin_platform = platform + } + + if (platformLabel) { + metadata.sm_origin_platform_label = platformLabel + } + + if (data.sourceSurface) { + metadata.sm_origin_surface = data.sourceSurface + } + if (data.ogImage) { metadata.website_og_image = data.ogImage } @@ -252,6 +312,9 @@ export default defineBackground(() => { const memoryData: MemoryData = { content: messageData.prompt, + url: messageData.source, + sourcePlatform: messageData.platform, + sourceSurface: "prompt_capture", } const result = await saveMemoryToSupermemory( diff --git a/apps/browser-extension/entrypoints/content/chatgpt.ts b/apps/browser-extension/entrypoints/content/chatgpt.ts index d6049cab..0ec50d92 100644 --- a/apps/browser-extension/entrypoints/content/chatgpt.ts +++ b/apps/browser-extension/entrypoints/content/chatgpt.ts @@ -341,6 +341,9 @@ async function saveMemoriesToSupermemory() { action: MESSAGE_TYPES.SAVE_MEMORY, data: { html: combinedContent, + sourcePlatform: "chatgpt", + sourceSurface: "memories_dialog", + url: window.location.href, }, actionSource: "chatgpt_memories_dialog", }) @@ -701,7 +704,7 @@ function setupChatGPTPromptCapture() { data: { prompt: promptContent, platform: "chatgpt", - source: source, + source: window.location.href, }, }) } catch (error) { diff --git a/apps/browser-extension/entrypoints/content/claude.ts b/apps/browser-extension/entrypoints/content/claude.ts index 5bd0d6e1..54c5ceb4 100644 --- a/apps/browser-extension/entrypoints/content/claude.ts +++ b/apps/browser-extension/entrypoints/content/claude.ts @@ -749,7 +749,7 @@ function setupClaudePromptCapture() { data: { prompt: promptContent, platform: "claude", - source: source, + source: window.location.href, }, }) } catch (error) { diff --git a/apps/browser-extension/entrypoints/content/gemini.ts b/apps/browser-extension/entrypoints/content/gemini.ts index 04c670a2..cf763c10 100644 --- a/apps/browser-extension/entrypoints/content/gemini.ts +++ b/apps/browser-extension/entrypoints/content/gemini.ts @@ -523,7 +523,7 @@ function setupGeminiPromptCapture() { data: { prompt: promptContent, platform: "gemini", - source, + source: window.location.href, }, }) debugGemini("capture response", response) diff --git a/apps/browser-extension/entrypoints/content/t3.ts b/apps/browser-extension/entrypoints/content/t3.ts index c7bdb09a..13ce233d 100644 --- a/apps/browser-extension/entrypoints/content/t3.ts +++ b/apps/browser-extension/entrypoints/content/t3.ts @@ -546,7 +546,7 @@ function setupT3PromptCapture() { data: { prompt: promptContent, platform: "t3", - source: source, + source: window.location.href, }, }) } catch (error) { diff --git a/apps/browser-extension/utils/types.ts b/apps/browser-extension/utils/types.ts index 8cec2241..e17f4ff9 100644 --- a/apps/browser-extension/utils/types.ts +++ b/apps/browser-extension/utils/types.ts @@ -38,6 +38,9 @@ export interface MemoryData { url?: string ogImage?: string title?: string + sourcePlatform?: string + sourcePlatformLabel?: string + sourceSurface?: string } /** diff --git a/apps/web/components/memories-grid.tsx b/apps/web/components/memories-grid.tsx index be363274..f8098148 100644 --- a/apps/web/components/memories-grid.tsx +++ b/apps/web/components/memories-grid.tsx @@ -101,6 +101,32 @@ type OgData = { image?: string } +const EXTENSION_PLATFORM_LABELS: Record = { + chatgpt: "ChatGPT", + claude: "Claude", + gemini: "Gemini", + t3: "T3 Chat", + twitter: "X / Twitter", +} + +function getExtensionSourceLabel(document: DocumentWithMemories): string | null { + const metadata = document.metadata + if (!metadata || typeof metadata !== "object") return null + + const label = metadata.sm_origin_platform_label + if (typeof label === "string" && label.trim()) { + return label.trim() + } + + const platform = metadata.sm_origin_platform + if (typeof platform === "string" && platform.trim()) { + const normalized = platform.trim().toLowerCase() + return EXTENSION_PLATFORM_LABELS[normalized] || platform.trim() + } + + return null +} + const ogCache = new Map() const ogInflight = new Map>() const ogFailures = new Map() @@ -1229,6 +1255,7 @@ const DocumentCard = memo( pluginDocument?.kind === "claude-code-doc" ? claudeCodeTokenBadge(document) : null + const sourceLabel = getExtensionSourceLabel(document) const date = new Date( document.createdAt, ).toLocaleDateString("en-US", { @@ -1236,7 +1263,7 @@ const DocumentCard = memo( day: "numeric", year: "numeric", }) - return badge ? `${badge} ยท ${date}` : date + return [sourceLabel, badge, date].filter(Boolean).join(" - ") })()}