diff --git a/apps/browser-extension/entrypoints/background.ts b/apps/browser-extension/entrypoints/background.ts index 12126115..81c67394 100644 --- a/apps/browser-extension/entrypoints/background.ts +++ b/apps/browser-extension/entrypoints/background.ts @@ -1,7 +1,8 @@ import { TwitterImporter, type TwitterImportConfig } from '../utils/twitter-import'; import { captureTwitterTokens } from '../utils/twitter-auth'; -import { CONTEXT_MENU_IDS, MESSAGE_TYPES, API_ENDPOINTS, CONTAINER_TAGS } from '../utils/constants'; +import { CONTEXT_MENU_IDS, MESSAGE_TYPES, CONTAINER_TAGS } from '../utils/constants'; import type { ExtensionMessage, MemoryPayload } from '../utils/types'; +import { getDefaultProject, saveMemory, searchMemories } from '../utils/api'; export default defineBackground(() => { let twitterImporter: TwitterImporter | null = null; @@ -69,34 +70,23 @@ export default defineBackground(() => { */ const saveMemoryToSupermemory = async (data: any): Promise<{ success: boolean; data?: any; error?: string }> => { try { - const result = await browser.storage.local.get(['bearerToken']); - const bearerToken = result.bearerToken; - - if (!bearerToken) { - return { success: false, error: 'No authentication token found' }; + 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: [CONTAINER_TAGS.DEFAULT_PROJECT], + containerTags: [containerTag], content: data.highlightedText + '\n\n' + data.html + '\n\n' + data?.url, metadata: { sm_source: 'consumer' }, }; - const response = await fetch(`${API_ENDPOINTS.SUPERMEMORY_API}/v3/memories`, { - method: 'POST', - credentials: 'omit', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${bearerToken}`, - }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - return { success: false, error: `API call failed: ${response.status}` }; - } - - const responseData = await response.json(); + const responseData = await saveMemory(payload); return { success: true, data: responseData }; } catch (error) { return { @@ -108,35 +98,11 @@ export default defineBackground(() => { const getRelatedMemories = async (data: any): Promise<{ success: boolean; data?: any; error?: string }> => { try { - const result = await browser.storage.local.get(['bearerToken']); - const bearerToken = result.bearerToken; - - if (!bearerToken) { - return { success: false, error: 'No authentication token found' }; - } - - const response = await fetch(`${API_ENDPOINTS.SUPERMEMORY_API}/v3/search`, { - method: 'POST', - credentials: 'omit', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${bearerToken}`, - }, - body: JSON.stringify({ - q: data - }), - }); - - if (!response.ok) { - return { success: false, error: `API call failed: ${response.status}` }; - } - - const responseData = await response.json(); + const responseData = await searchMemories(data); const content = responseData.results[0].chunks[0].content; console.log('Content:', content); return { success: true, data: content }; - } - catch(error){ + } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error', diff --git a/apps/browser-extension/entrypoints/popup/App.css b/apps/browser-extension/entrypoints/popup/App.css index d088d98b..eab4fe8f 100644 --- a/apps/browser-extension/entrypoints/popup/App.css +++ b/apps/browser-extension/entrypoints/popup/App.css @@ -1,7 +1,8 @@ .popup-container { width: 320px; padding: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-family: + -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #ffffff; border-radius: 8px; } @@ -78,7 +79,8 @@ line-height: 1.4; } -.authenticated, .unauthenticated { +.authenticated, +.unauthenticated { text-align: left; } @@ -114,3 +116,170 @@ height: 20px; flex-shrink: 0; } + +/* Project Selection Styles */ +.project-section { + margin-bottom: 16px; + padding: 10px; + background-color: #f8f9fa; + border-radius: 3%; + border: 1px solid #e9ecef; +} + +.project-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.project-label { + font-size: 13px; + font-weight: 500; + color: #495057; +} + +.project-change-btn { + padding: 4px 8px; + background-color: #ffffff; + color: #000000; + border: 1px solid #ced4da; + border-radius: 4px; + font-size: 12px; + cursor: pointer; + transition: background-color 0.2s ease; +} + +.project-change-btn:hover { + background-color: #f8f9fa; +} + +.project-current { + padding: 4px 0; +} + +.project-info { + display: flex; + justify-content: space-between; + align-items: center; +} + +.project-name { + font-size: 14px; + font-weight: 500; + color: #000000; + flex: 1; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.project-count { + font-size: 12px; + color: #6c757d; + margin-left: 8px; +} + +.project-none { + font-size: 14px; + color: #6c757d; + font-style: italic; +} + +/* Project Selector Modal */ +.project-selector { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ffffff; + border-radius: 8px; + z-index: 100; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +.project-selector-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; + border-bottom: 1px solid #e5e7eb; + font-size: 16px; + font-weight: 600; +} + +.project-close-btn { + background: none; + border: none; + font-size: 20px; + cursor: pointer; + color: #6c757d; + padding: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} + +.project-close-btn:hover { + color: #000000; +} + +.project-loading { + padding: 32px 16px; + text-align: center; + color: #6c757d; + font-size: 14px; +} + +.project-list { + max-height: 240px; + overflow-y: auto; +} + +.project-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 16px; + cursor: pointer; + transition: background-color 0.2s ease; + border-bottom: 1px solid #f1f3f4; +} + +.project-item:hover { + background-color: #f8f9fa; +} + +.project-item:last-child { + border-bottom: none; +} + +.project-item.selected { + background-color: #e3f2fd; +} + +.project-item-info { + display: flex; + flex-direction: column; + flex: 1; + gap: 2px; +} + +.project-item-name { + font-size: 14px; + font-weight: 500; + color: #000000; +} + +.project-item-count { + font-size: 12px; + color: #6c757d; +} + +.project-item-check { + color: #1976d2; + font-weight: bold; + font-size: 16px; +} diff --git a/apps/browser-extension/entrypoints/popup/App.tsx b/apps/browser-extension/entrypoints/popup/App.tsx index 84691a57..796a42be 100644 --- a/apps/browser-extension/entrypoints/popup/App.tsx +++ b/apps/browser-extension/entrypoints/popup/App.tsx @@ -1,15 +1,31 @@ import React, { useState, useEffect } from 'react'; import './App.css'; +import { getProjects, getDefaultProject, setDefaultProject } from '../../utils/api'; +import { Project } from '../../utils/types'; function App() { const [userSignedIn, setUserSignedIn] = useState(false); const [loading, setLoading] = useState(true); + const [projects, setProjects] = useState([]); + const [defaultProject, setDefaultProjectState] = useState(null); + const [loadingProjects, setLoadingProjects] = useState(false); + const [showProjectSelector, setShowProjectSelector] = useState(false); useEffect(() => { const checkAuthStatus = async () => { try { const result = await chrome.storage.local.get(['bearerToken']); - setUserSignedIn(!!result.bearerToken); + const isSignedIn = !!result.bearerToken; + setUserSignedIn(isSignedIn); + + if (isSignedIn) { + try { + const defaultProj = await getDefaultProject(); + setDefaultProjectState(defaultProj); + } catch (error) { + console.error('Error loading default project:', error); + } + } } catch (error) { console.error('Error checking auth status:', error); setUserSignedIn(false); @@ -30,6 +46,44 @@ function App() { } }; + const loadProjects = async () => { + setLoadingProjects(true); + try { + const projectsList = await getProjects(); + setProjects(projectsList); + console.log('Projects:', projectsList); + console.log('Default project:', defaultProject); + // If no default project is set and projects are available, set first as default + if (!defaultProject && projectsList.length > 0) { + const firstProject = projectsList[0]; + await setDefaultProject(firstProject); + setDefaultProjectState(firstProject); + } + } catch (error) { + console.error('Error loading projects:', error); + } finally { + setLoadingProjects(false); + } + }; + + const handleProjectSelect = async (project: Project) => { + try { + await setDefaultProject(project); + setDefaultProjectState(project); + setShowProjectSelector(false); + } catch (error) { + console.error('Error setting default project:', error); + } + }; + + const handleShowProjectSelector = () => { + console.log('handleShowProjectSelector, projects.length:', projects.length); + if (projects.length === 0) { + loadProjects(); + } + setShowProjectSelector(true); + }; + if (loading) { return (
@@ -53,10 +107,63 @@ function App() {
{userSignedIn ? (
-
- - Signed in + +
+
+ Default Project: + +
+
+ {defaultProject ? ( +
+ {defaultProject.name} +
+ ) : ( + No project selected + )} +
+ + {showProjectSelector && ( +
+
+ Select Default Project + +
+ {loadingProjects ? ( +
Loading projects...
+ ) : ( +
+ {projects.map((project) => ( +
handleProjectSelect(project)} + > +
+ {project.name} + {project.documentCount} docs +
+ {defaultProject?.id === project.id && ( + + )} +
+ ))} +
+ )} +
+ )} +