import React, { useEffect, useRef, useState } from "react"; import { Readability } from "@mozilla/readability"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./ui/shadcn/tooltip"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/shadcn/popover"; import { Toaster } from "./ui/shadcn/toaster"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from "./ui/shadcn/select"; import { useToast } from "./ui/shadcn/use-toast"; import { Input } from "./ui/shadcn/input"; import { Label } from "./ui/shadcn/label"; import { Textarea } from "./ui/shadcn/textarea"; import ShowCommandMenu from "./showCommandMenu"; const BACKEND_URL = "https://supermemory.ai"; export default function ContentApp({ token, shadowRoot, }: { token: string | undefined; shadowRoot: ShadowRoot; }) { const [hover, setHover] = useState(false); const { toast } = useToast(); const [loading, setLoading] = useState(false); const [webNote, setWebNote] = useState(""); const [importedMessage, setImportedMessage] = useState("Importing..."); const [isImporting, setIsImporting] = useState(false); const [importDone, setImportDone] = useState(false); const [portalContainer, setPortalContainer] = useState( null, ); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [isPopover2Open, setIsPopover2Open] = useState(false); const [spacesOptions, setSpacesOptions] = useState< { id: number; name: string }[] >([]); const [selectedSpace, setSelectedSpace] = useState(); const [userNotLoggedIn, setUserNotLoggedIn] = useState(false); const showLoginToast = async () => { setUserNotLoggedIn(true); const NOSHOW_TOAST = ["accounts.google.com", "supermemory.ai"]; const noLoginWarning = await chrome.storage.local.get("noLoginWarning"); if (Object.keys(noLoginWarning).length > 0) { return; } if (!NOSHOW_TOAST.includes(window.location.host)) { const t = toast({ title: "Please login to supermemory.ai to use this extension.", action: (
), }); } }; const [timer, setTimer] = useState(null); const [progress, setProgress] = useState(0); type TimerId = ReturnType; const timerRef = useRef(null); const saveTimeoutRef = useRef(null); useEffect(() => { if (isPopoverOpen) { startTimer(); } else { stopTimer(); } return () => { stopTimer(); if (saveTimeoutRef.current) { clearTimeout(saveTimeoutRef.current); } }; }, [isPopoverOpen]); const startTimer = () => { stopTimer(); setProgress(0); timerRef.current = setInterval(() => { setProgress((prev) => { if (prev >= 100) { stopTimer(); scheduleSave(); return 100; } return prev + 5; }); }, 100); }; const stopTimer = () => { if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; } }; const scheduleSave = () => { if (saveTimeoutRef.current) { clearTimeout(saveTimeoutRef.current); } saveTimeoutRef.current = setTimeout(() => { saveContent(); saveTimeoutRef.current = null; }, 500); }; const saveContent = async () => { await sendUrlToAPI(selectedSpace ? [selectedSpace] : []); }; const handleInputChange = ( e: React.ChangeEvent, ) => { setWebNote(e.target.value); stopTimer(); }; const handleSelectChange = (value: string) => { setSelectedSpace(value); stopTimer(); }; useEffect(() => { document.addEventListener("mousemove", (e) => { const percentageX = (e.clientX / window.innerWidth) * 100; const percentageY = (e.clientY / window.innerHeight) * 100; if (percentageX > 75 && percentageY > 75) { setHover(true); } else { setHover(false); } }); const getUserData = () => { chrome.runtime.sendMessage({ type: "getJwt" }); }; getUserData(); chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === "import-update") { setIsImporting(true); setImportedMessage(request.importedMessage); } if (request.type === "import-done") { setIsImporting(false); setImportDone(true); } if (request.type === "supermemory-message") { toast({ title: request.message, }); } }); const handleKeyDown = (e: KeyboardEvent) => { if (isPopoverOpen) { e.stopPropagation(); e.preventDefault(); } }; document.addEventListener("keydown", handleKeyDown, true); const portalDiv = document.createElement("div"); portalDiv.id = "popover-portal"; shadowRoot.appendChild(portalDiv); setPortalContainer(portalDiv); return () => { document.removeEventListener("mousemove", () => {}); document.removeEventListener("keydown", handleKeyDown, true); }; }, []); const getSpaces = async () => { const response = await fetch(`${BACKEND_URL}/api/spaces`, { headers: { Authorization: `Bearer ${token}`, }, }); if (response.status === 401) { showLoginToast(); return; } try { const data = await response.json(); setSpacesOptions(data.data); } catch (e) { console.error( `Error in supermemory.ai extension: ${e}. Please contact the developer https://x.com/dhravyashah`, ); } }; async function sendUrlToAPI(spaces: string[]) { setLoading(true); setTimeout(() => { setLoading(false); }, 1500); // get the current URL const url = window.location.href; const blacklist: string[] = []; // check if the URL is blacklisted if (blacklist.some((blacklisted) => url.includes(blacklisted))) { return; } else { const clone = document.cloneNode(true) as Document; const article = new Readability(clone).parse(); const ogImage = document .querySelector('meta[property="og:image"]') ?.getAttribute("content"); const favicon = ( document.querySelector('link[rel="icon"]') as HTMLLinkElement )?.href; setLoading(true); setIsPopoverOpen(false); await fetch(`${BACKEND_URL}/api/store`, { method: "POST", headers: { Authorization: `Bearer ${token}`, }, body: JSON.stringify({ pageContent: (webNote ? `Note about this website: ${webNote}\n\n` : "") + article?.textContent, url: url + "#supermemory-user-" + Math.random(), title: article?.title.slice(0, 500), spaces: spaces, description: article?.excerpt.slice(0, 250), ogImage: ogImage?.slice(0, 1000), image: favicon, }), }).then(async (rep) => { if (rep.status === 401) { showLoginToast(); return; } const d = await rep.json(); if (rep.status === 200) { toast({ title: "Saved to supermemory.ai", action: ( ), }); } else { toast({ title: `Failed to save to supermemory.ai: ${d.error ?? "Unknown error"}`, }); } setLoading(false); return rep; }); } } if (!shadowRoot || !portalContainer) { return null; } return (
setIsPopoverOpen(!isPopoverOpen)} > await getSpaces()} asChild> {userNotLoggedIn ? ( <>You need to login to use this extension. ) : (

Add to supermemory.ai

)}
{userNotLoggedIn ? (
) : (
Saving to supermemory.ai