supermemory/apps/browser-extension/entrypoints/background.ts
Dhravya 65e8c5d525 changes to chrome extension (#402)
### TL;DR

Added proper semicolons throughout the codebase, improved Twitter import functionality, enhanced memory search results display, and added various icon sizes for the browser extension.

### What changed?

- Added semicolons at the end of statements throughout the browser extension codebase for consistent code style
- Fixed the Twitter import button to only appear on the bookmarks page
- Updated the saveMemoriesToSupermemory function to use the correct action type
- Enhanced the memory search functionality to include related memories in search results
- Added an expandable UI component in the chat interface to display memory search results with details
- Updated the welcome page text to better describe supermemory's capabilities
- Added multiple icon sizes (48x48 to 512x512) for better browser extension display across platforms
- Updated the sign-in button text from "login in" to "Sign in or create account"
- Added turbo flag to the Next.js dev script for faster development

### How to test?

1. Test the Twitter import functionality by visiting the Twitter bookmarks page
2. Verify that the memory search functionality works correctly in chat interfaces
3. Check that the expandable memory results UI displays properly with memory details
4. Confirm that the browser extension icons display correctly at different sizes
5. Verify the sign-in flow with the updated button text

### Why make this change?

This change improves code consistency by standardizing semicolon usage throughout the codebase. It enhances the user experience by making the Twitter import feature more targeted and fixing the memory saving functionality. The expandable memory results UI provides users with more detailed information about their memories, making the search functionality more useful. The additional icon sizes ensure the extension displays properly across different platforms and contexts. Overall, these changes improve both the developer experience through consistent code style and the user experience through enhanced functionality and clearer UI elements.
2025-09-01 06:02:37 +00:00

201 lines
5.2 KiB
TypeScript

import { getDefaultProject, saveMemory, searchMemories } from "../utils/api";
import {
CONTAINER_TAGS,
CONTEXT_MENU_IDS,
MESSAGE_TYPES,
} from "../utils/constants";
import { captureTwitterTokens } from "../utils/twitter-auth";
import {
type TwitterImportConfig,
TwitterImporter,
} from "../utils/twitter-import";
import type {
ExtensionMessage,
MemoryData,
MemoryPayload,
} from "../utils/types";
export default defineBackground(() => {
let twitterImporter: TwitterImporter | null = null;
browser.runtime.onInstalled.addListener((details) => {
browser.contextMenus.create({
id: CONTEXT_MENU_IDS.SAVE_TO_SUPERMEMORY,
title: "Save to supermemory",
contexts: ["selection", "page", "link"],
});
// Open welcome tab on first install
if (details.reason === "install") {
browser.tabs.create({
url: browser.runtime.getURL("/welcome.html"),
});
}
});
// Intercept Twitter requests to capture authentication headers.
browser.webRequest.onBeforeSendHeaders.addListener(
(details) => {
captureTwitterTokens(details);
return {};
},
{ urls: ["*://x.com/*", "*://twitter.com/*"] },
["requestHeaders", "extraHeaders"],
);
// Handle context menu clicks.
browser.contextMenus.onClicked.addListener(async (info, tab) => {
if (info.menuItemId === CONTEXT_MENU_IDS.SAVE_TO_SUPERMEMORY) {
if (tab?.id) {
try {
await browser.tabs.sendMessage(tab.id, {
action: MESSAGE_TYPES.SAVE_MEMORY,
});
} catch (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
*/
const sendImportDoneMessage = async (totalImported: number) => {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
if (tabs.length > 0 && tabs[0].id) {
await browser.tabs.sendMessage(tabs[0].id, {
type: MESSAGE_TYPES.IMPORT_DONE,
totalImported,
});
}
};
/**
* Save memory to supermemory API
*/
const saveMemoryToSupermemory = async (
data: MemoryData,
): Promise<{ success: boolean; data?: unknown; error?: string }> => {
try {
let containerTag: string = CONTAINER_TAGS.DEFAULT_PROJECT;
try {
const defaultProject = await getDefaultProject();
if (defaultProject?.containerTag) {
containerTag = defaultProject.containerTag;
}
} catch (error) {
console.warn("Failed to get default project, using fallback:", error);
}
const payload: MemoryPayload = {
containerTags: [containerTag],
content: `${data.highlightedText}\n\n${data.html}\n\n${data?.url}`,
metadata: { sm_source: "consumer" },
};
const responseData = await saveMemory(payload);
return { success: true, data: responseData };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
};
const getRelatedMemories = async (
data: string,
): Promise<{ success: boolean; data?: unknown; error?: string }> => {
try {
const responseData = await searchMemories(data)
const response = responseData as {
results?: Array<{ memory?: string }>
}
let memories = "";
response.results?.forEach((result, index) => {
memories += `[${index + 1}] ${result.memory} `
})
console.log("Memories:", memories)
return { success: true, data: memories }
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
}
}
};
/**
* Handle extension messages
*/
browser.runtime.onMessage.addListener(
(message: ExtensionMessage, _sender, sendResponse) => {
// Handle Twitter import request
if (message.type === MESSAGE_TYPES.BATCH_IMPORT_ALL) {
const importConfig: TwitterImportConfig = {
onProgress: sendMessageToCurrentTab,
onComplete: sendImportDoneMessage,
onError: async (error: Error) => {
await sendMessageToCurrentTab(`Error: ${error.message}`);
},
};
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 () => {
try {
const result = await saveMemoryToSupermemory(
message.data as MemoryData,
);
sendResponse(result);
} catch (error) {
sendResponse({
success: false,
error: error instanceof Error ? error.message : "Unknown error",
});
}
})();
return true;
}
if (message.action === MESSAGE_TYPES.GET_RELATED_MEMORIES) {
(async () => {
try {
const result = await getRelatedMemories(message.data as string);
sendResponse(result);
} catch (error) {
sendResponse({
success: false,
error: error instanceof Error ? error.message : "Unknown error",
});
}
})();
return true;
}
},
);
});