From 0b543bb1d67782bca624362e4efd49791d6b2763 Mon Sep 17 00:00:00 2001 From: Dhravya Date: Thu, 4 Jul 2024 10:49:42 -0500 Subject: [PATCH 01/10] better-extension --- apps/extension/background.ts | 11 + apps/extension/components.json | 18 + apps/extension/content/ContentApp.tsx | 142 +++-- apps/extension/content/base.css | 4 - apps/extension/content/content.tsx | 13 +- apps/extension/content/ui/shadcn/popover.tsx | 38 ++ apps/extension/content/ui/shadcn/toast.tsx | 129 ++++ apps/extension/content/ui/shadcn/toaster.tsx | 35 ++ apps/extension/content/ui/shadcn/tooltip.tsx | 30 + apps/extension/content/ui/shadcn/use-toast.ts | 191 ++++++ apps/extension/content/utils.ts | 6 + apps/extension/package.json | 22 +- apps/extension/public/output.css | 572 +++++++----------- apps/extension/tailwind.config.js | 6 - apps/extension/tailwind.config.ts | 1 + apps/web/app/(auth)/auth-buttons.tsx | 15 - apps/web/app/(auth)/signin/page.tsx | 8 +- apps/web/app/(dash)/chat/chatWindow.tsx | 4 +- apps/web/app/(dash)/home/page.tsx | 9 +- apps/web/tsconfig.json | 2 +- package.json | 1 + packages/tailwind-config/globals.css | 34 +- packages/tailwind-config/tailwind.config.ts | 4 +- 23 files changed, 840 insertions(+), 455 deletions(-) create mode 100644 apps/extension/components.json create mode 100644 apps/extension/content/ui/shadcn/popover.tsx create mode 100644 apps/extension/content/ui/shadcn/toast.tsx create mode 100644 apps/extension/content/ui/shadcn/toaster.tsx create mode 100644 apps/extension/content/ui/shadcn/tooltip.tsx create mode 100644 apps/extension/content/ui/shadcn/use-toast.ts create mode 100644 apps/extension/content/utils.ts delete mode 100644 apps/extension/tailwind.config.js create mode 100644 apps/extension/tailwind.config.ts delete mode 100644 apps/web/app/(auth)/auth-buttons.tsx diff --git a/apps/extension/background.ts b/apps/extension/background.ts index 1102b764..97bb341c 100644 --- a/apps/extension/background.ts +++ b/apps/extension/background.ts @@ -311,7 +311,18 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { }).then((ers) => console.log(ers.status)); }); })(); + + return true; } else if (request.type === "batchImportAll") { batchImportAll(); } }); + +chrome.runtime.onInstalled.addListener(function (details) { + if (details.reason === "install") { + chrome.tabs.create({ + url: "https://supermemory.ai/signin?extension=true", + active: true, + }); + } +}); diff --git a/apps/extension/components.json b/apps/extension/components.json new file mode 100644 index 00000000..d3099362 --- /dev/null +++ b/apps/extension/components.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "../../packages/tailwind-config/globals.css", + "baseColor": "stone", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "content/ui/components", + "utils": "content/utils", + "ui": "content/ui/shadcn" + } +} diff --git a/apps/extension/content/ContentApp.tsx b/apps/extension/content/ContentApp.tsx index 4fe2bc66..c1af1dfc 100644 --- a/apps/extension/content/ContentApp.tsx +++ b/apps/extension/content/ContentApp.tsx @@ -1,8 +1,25 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { Readability } from "@mozilla/readability"; import tailwindBg from "../public/tailwind_bg.png"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "./ui/shadcn/tooltip"; +import { Popover, PopoverContent, PopoverTrigger } from "./ui/shadcn/popover"; +import { Toaster } from "./ui/shadcn/toaster"; +import { useToast } from "./ui/shadcn/use-toast"; -export default function ContentApp({ token }: { token: string | undefined }) { +const BACKEND_URL = "https://supermemory.ai"; + +export default function ContentApp({ + token, + shadowRoot, +}: { + token: string | undefined; + shadowRoot: ShadowRoot; +}) { const [text, setText] = useState(""); const [hover, setHover] = useState(false); @@ -11,6 +28,12 @@ export default function ContentApp({ token }: { token: string | undefined }) { const [importedCount, setImportedCount] = useState(0); const [isImporting, setIsImporting] = useState(false); + const [portalContainer, setPortalContainer] = useState( + null, + ); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const { toast } = useToast(); + useEffect(() => { document.addEventListener("mousemove", (e) => { const percentageX = (e.clientX / window.innerWidth) * 100; @@ -24,16 +47,7 @@ export default function ContentApp({ token }: { token: string | undefined }) { }); const getUserData = () => { - const NO_JWT = [ - "supermemory.ai", - "beta.supermemory.ai", - "localhost:3000", - ]; chrome.runtime.sendMessage({ type: "getJwt" }, (response) => { - if (!response.jwt && !NO_JWT.includes(window.location.host)) { - window.location.href = "https://supermemory.ai/signin"; - } - console.log("jwt", response.jwt); }); }; @@ -54,10 +68,15 @@ export default function ContentApp({ token }: { token: string | undefined }) { } }); + const portalDiv = document.createElement("div"); + portalDiv.id = "popover-portal"; + shadowRoot.appendChild(portalDiv); + setPortalContainer(portalDiv); + return () => { document.removeEventListener("mousemove", () => {}); }; - }, []); + }, [document.getElementById("supermemory-extension-host")]); function sendUrlToAPI(spaces: number[]) { setLoading(true); @@ -100,55 +119,60 @@ export default function ContentApp({ token }: { token: string | undefined }) { } } - return ( -
- + if (!shadowRoot || !portalContainer) { + return null; + } - {isImporting && ( -
-
-
-
- -
-
-
-

- Your twitter bookmarks are being imported. -

-

- This is done completely locally, so please keep this tab open! -

-
- Imported {importedCount} bookmarks + return ( +
+ + + + + + + + {loading ? ( +
Saving...
+ ) : ( + + + + )} +
+
+ +

Add to supermemory.ai

+
+
+
+ +
+

Select a space

+ +
-
- )} + + {(window.location.host === "twitter.com" || window.location.host === "x.com") && ( diff --git a/apps/extension/content/base.css b/apps/extension/content/base.css index c8845aea..cd7633f5 100644 --- a/apps/extension/content/base.css +++ b/apps/extension/content/base.css @@ -1,7 +1,3 @@ -/* @tailwind base; -@tailwind components; -@tailwind utilities; */ - #supermemory-extension-host { position: fixed; bottom: 0; diff --git a/apps/extension/content/content.tsx b/apps/extension/content/content.tsx index bacb79c0..f44beacb 100644 --- a/apps/extension/content/content.tsx +++ b/apps/extension/content/content.tsx @@ -8,10 +8,11 @@ const TAILWIND_URL = "https://cdn.jsdelivr.net/npm/tailwindcss@^2.0/dist/tailwind.min.css"; const appendTailwindStyleData = (shadowRoot: ShadowRoot) => { - // Load and inject the local Tailwind CSS file into the shadow DOM const styleSheet = document.createElement("style"); - // Change this path to your actual Tailwind CSS file's location - fetch(TAILWIND_URL) + + const path = chrome.runtime.getURL("../public/output.css"); + + fetch(path) .then((response) => response.text()) .then((css) => { styleSheet.textContent = css; @@ -44,7 +45,7 @@ function initial() { rootDiv.id = "supermemory-extension-root"; shadowRoot.appendChild(rootDiv); - appendTailwindStyleLink(shadowRoot); + appendTailwindStyleData(shadowRoot); const root = ReactDOM.createRoot(rootDiv); @@ -52,7 +53,9 @@ function initial() { return data.jwt; }) as Promise; - jwt.then((token) => root.render()); + jwt.then((token) => + root.render(), + ); } window.addEventListener("message", (event) => { diff --git a/apps/extension/content/ui/shadcn/popover.tsx b/apps/extension/content/ui/shadcn/popover.tsx new file mode 100644 index 00000000..e896283e --- /dev/null +++ b/apps/extension/content/ui/shadcn/popover.tsx @@ -0,0 +1,38 @@ +"use client"; + +import * as React from "react"; +import * as PopoverPrimitive from "@radix-ui/react-popover"; + +import { cn } from "../../utils"; + +const Popover = PopoverPrimitive.Root; + +const PopoverTrigger = PopoverPrimitive.Trigger; + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + container?: Element; + } +>( + ( + { className, container, align = "center", sideOffset = 4, ...props }, + ref, + ) => ( + + + + ), +); +PopoverContent.displayName = PopoverPrimitive.Content.displayName; + +export { Popover, PopoverTrigger, PopoverContent }; diff --git a/apps/extension/content/ui/shadcn/toast.tsx b/apps/extension/content/ui/shadcn/toast.tsx new file mode 100644 index 00000000..b0f93ac4 --- /dev/null +++ b/apps/extension/content/ui/shadcn/toast.tsx @@ -0,0 +1,129 @@ +"use client"; + +import * as React from "react"; +import * as ToastPrimitives from "@radix-ui/react-toast"; +import { cva, type VariantProps } from "class-variance-authority"; +import { X } from "lucide-react"; + +import { cn } from "../../utils"; + +const ToastProvider = ToastPrimitives.Provider; + +const ToastViewport = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastViewport.displayName = ToastPrimitives.Viewport.displayName; + +const toastVariants = cva( + "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", + { + variants: { + variant: { + default: "border bg-background text-foreground", + destructive: + "destructive group border-destructive bg-destructive text-destructive-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +const Toast = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, ...props }, ref) => { + return ( + + ); +}); +Toast.displayName = ToastPrimitives.Root.displayName; + +const ToastAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastAction.displayName = ToastPrimitives.Action.displayName; + +const ToastClose = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +ToastClose.displayName = ToastPrimitives.Close.displayName; + +const ToastTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastTitle.displayName = ToastPrimitives.Title.displayName; + +const ToastDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastDescription.displayName = ToastPrimitives.Description.displayName; + +type ToastProps = React.ComponentPropsWithoutRef; + +type ToastActionElement = React.ReactElement; + +export { + type ToastProps, + type ToastActionElement, + ToastProvider, + ToastViewport, + Toast, + ToastTitle, + ToastDescription, + ToastClose, + ToastAction, +}; diff --git a/apps/extension/content/ui/shadcn/toaster.tsx b/apps/extension/content/ui/shadcn/toaster.tsx new file mode 100644 index 00000000..9b494bfd --- /dev/null +++ b/apps/extension/content/ui/shadcn/toaster.tsx @@ -0,0 +1,35 @@ +"use client"; + +import { + Toast, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, +} from "../shadcn/toast"; +import { useToast } from "../shadcn/use-toast"; + +export function Toaster() { + const { toasts } = useToast(); + + return ( + + {toasts.map(function ({ id, title, description, action, ...props }) { + return ( + +
+ {title && {title}} + {description && ( + {description} + )} +
+ {action} + +
+ ); + })} + +
+ ); +} diff --git a/apps/extension/content/ui/shadcn/tooltip.tsx b/apps/extension/content/ui/shadcn/tooltip.tsx new file mode 100644 index 00000000..b3534220 --- /dev/null +++ b/apps/extension/content/ui/shadcn/tooltip.tsx @@ -0,0 +1,30 @@ +"use client"; + +import * as React from "react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; + +import { cn } from "../../utils"; + +const TooltipProvider = TooltipPrimitive.Provider; + +const Tooltip = TooltipPrimitive.Root; + +const TooltipTrigger = TooltipPrimitive.Trigger; + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/apps/extension/content/ui/shadcn/use-toast.ts b/apps/extension/content/ui/shadcn/use-toast.ts new file mode 100644 index 00000000..2f3870d8 --- /dev/null +++ b/apps/extension/content/ui/shadcn/use-toast.ts @@ -0,0 +1,191 @@ +"use client"; + +// Inspired by react-hot-toast library +import * as React from "react"; + +import type { ToastActionElement, ToastProps } from "content/ui/shadcn/toast"; + +const TOAST_LIMIT = 1; +const TOAST_REMOVE_DELAY = 1000000; + +type ToasterToast = ToastProps & { + id: string; + title?: React.ReactNode; + description?: React.ReactNode; + action?: ToastActionElement; +}; + +const actionTypes = { + ADD_TOAST: "ADD_TOAST", + UPDATE_TOAST: "UPDATE_TOAST", + DISMISS_TOAST: "DISMISS_TOAST", + REMOVE_TOAST: "REMOVE_TOAST", +} as const; + +let count = 0; + +function genId() { + count = (count + 1) % Number.MAX_SAFE_INTEGER; + return count.toString(); +} + +type ActionType = typeof actionTypes; + +type Action = + | { + type: ActionType["ADD_TOAST"]; + toast: ToasterToast; + } + | { + type: ActionType["UPDATE_TOAST"]; + toast: Partial; + } + | { + type: ActionType["DISMISS_TOAST"]; + toastId?: ToasterToast["id"]; + } + | { + type: ActionType["REMOVE_TOAST"]; + toastId?: ToasterToast["id"]; + }; + +interface State { + toasts: ToasterToast[]; +} + +const toastTimeouts = new Map>(); + +const addToRemoveQueue = (toastId: string) => { + if (toastTimeouts.has(toastId)) { + return; + } + + const timeout = setTimeout(() => { + toastTimeouts.delete(toastId); + dispatch({ + type: "REMOVE_TOAST", + toastId: toastId, + }); + }, TOAST_REMOVE_DELAY); + + toastTimeouts.set(toastId, timeout); +}; + +export const reducer = (state: State, action: Action): State => { + switch (action.type) { + case "ADD_TOAST": + return { + ...state, + toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), + }; + + case "UPDATE_TOAST": + return { + ...state, + toasts: state.toasts.map((t) => + t.id === action.toast.id ? { ...t, ...action.toast } : t, + ), + }; + + case "DISMISS_TOAST": { + const { toastId } = action; + + // ! Side effects ! - This could be extracted into a dismissToast() action, + // but I'll keep it here for simplicity + if (toastId) { + addToRemoveQueue(toastId); + } else { + state.toasts.forEach((toast) => { + addToRemoveQueue(toast.id); + }); + } + + return { + ...state, + toasts: state.toasts.map((t) => + t.id === toastId || toastId === undefined + ? { + ...t, + open: false, + } + : t, + ), + }; + } + case "REMOVE_TOAST": + if (action.toastId === undefined) { + return { + ...state, + toasts: [], + }; + } + return { + ...state, + toasts: state.toasts.filter((t) => t.id !== action.toastId), + }; + } +}; + +const listeners: Array<(state: State) => void> = []; + +let memoryState: State = { toasts: [] }; + +function dispatch(action: Action) { + memoryState = reducer(memoryState, action); + listeners.forEach((listener) => { + listener(memoryState); + }); +} + +type Toast = Omit; + +function toast({ ...props }: Toast) { + const id = genId(); + + const update = (props: ToasterToast) => + dispatch({ + type: "UPDATE_TOAST", + toast: { ...props, id }, + }); + const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }); + + dispatch({ + type: "ADD_TOAST", + toast: { + ...props, + id, + open: true, + onOpenChange: (open) => { + if (!open) dismiss(); + }, + }, + }); + + return { + id: id, + dismiss, + update, + }; +} + +function useToast() { + const [state, setState] = React.useState(memoryState); + + React.useEffect(() => { + listeners.push(setState); + return () => { + const index = listeners.indexOf(setState); + if (index > -1) { + listeners.splice(index, 1); + } + }; + }, [state]); + + return { + ...state, + toast, + dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), + }; +} + +export { useToast, toast }; diff --git a/apps/extension/content/utils.ts b/apps/extension/content/utils.ts new file mode 100644 index 00000000..365058ce --- /dev/null +++ b/apps/extension/content/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/apps/extension/package.json b/apps/extension/package.json index f23759d5..e53d0787 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -2,19 +2,31 @@ "devDependencies": { "@types/react": "^18.0.9", "@types/react-dom": "^18.0.5", + "@webpack-cli/generators": "^3.0.7", + "extension": "latest", "react": "^18.1.0", "react-dom": "^18.1.0", "tailwindcss": "^3.4.1", "typescript": "5.3.3", - "extension": "latest" + "webpack-cli": "^5.1.4" }, "scripts": { "dev": "extension dev", "start": "extension start", - "build": "extension build" + "build": "extension build", + "style": "tailwindcss -i ../../packages/tailwind-config/globals.css -o ./public/output.css --watch" }, - "dependencies": {}, - "name": "content-shadow-dom-tailwind", + "name": "supermemory-extension", "private": true, - "version": "0.0.0" + "version": "0.0.0", + "dependencies": { + "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-toast": "^1.2.1", + "@radix-ui/react-tooltip": "^1.1.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "lucide-react": "^0.400.0", + "tailwind-merge": "^2.3.0", + "tailwindcss-animate": "^1.0.7" + } } diff --git a/apps/extension/public/output.css b/apps/extension/public/output.css index d7852ac3..038be1ea 100644 --- a/apps/extension/public/output.css +++ b/apps/extension/public/output.css @@ -564,14 +564,6 @@ video { --tw-contain-style: ; } -.\!container { - width: 100% !important; - margin-right: auto !important; - margin-left: auto !important; - padding-right: 2rem !important; - padding-left: 2rem !important; -} - .container { width: 100%; margin-right: auto; @@ -581,10 +573,6 @@ video { } @media (min-width: 1400px) { - .\!container { - max-width: 1400px !important; - } - .container { max-width: 1400px; } @@ -610,22 +598,6 @@ video { pointer-events: auto; } -.visible { - visibility: visible; -} - -.invisible { - visibility: hidden; -} - -.collapse { - visibility: collapse; -} - -.static { - position: static; -} - .fixed { position: fixed; } @@ -638,10 +610,6 @@ video { position: relative; } -.sticky { - position: sticky; -} - .inset-0 { inset: 0px; } @@ -655,30 +623,18 @@ video { bottom: 0px; } -.bottom-12 { - bottom: 3rem; +.bottom-20 { + bottom: 5rem; } -.bottom-4 { - bottom: 1rem; -} - -.left-0 { - left: 0px; -} - -.left-1\/2 { - left: 50%; +.bottom-6 { + bottom: 1.5rem; } .left-2 { left: 0.5rem; } -.left-8 { - left: 2rem; -} - .left-\[50\%\] { left: 50%; } @@ -691,10 +647,6 @@ video { right: 1rem; } -.right-8 { - right: 2rem; -} - .top-0 { top: 0px; } @@ -719,36 +671,23 @@ video { z-index: 10; } +.z-30 { + z-index: 30; +} + .z-50 { z-index: 50; } -.z-\[1000000\] { - z-index: 1000000; -} - -.z-\[100000\] { - z-index: 100000; -} - .z-\[100\] { z-index: 100; } -.m-2 { - margin: 0.5rem; -} - .-mx-1 { margin-left: -0.25rem; margin-right: -0.25rem; } -.mx-4 { - margin-left: 1rem; - margin-right: 1rem; -} - .mx-auto { margin-left: auto; margin-right: auto; @@ -759,11 +698,6 @@ video { margin-bottom: 0.25rem; } -.my-2 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} - .mb-1 { margin-bottom: 0.25rem; } @@ -796,32 +730,10 @@ video { margin-top: auto; } -.line-clamp-1 { - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; -} - -.line-clamp-3 { - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 3; -} - -.\!block { - display: block !important; -} - .block { display: block; } -.inline { - display: inline; -} - .flex { display: flex; } @@ -830,35 +742,29 @@ video { display: inline-flex; } -.\!table { - display: table !important; -} - -.table { - display: table; -} - -.\!grid { - display: grid !important; -} - .grid { display: grid; } -.contents { - display: contents; -} - .hidden { display: none; } +.size-12 { + width: 3rem; + height: 3rem; +} + .size-4 { width: 1rem; height: 1rem; } +.size-8 { + width: 2rem; + height: 2rem; +} + .h-10 { height: 2.5rem; } @@ -867,10 +773,6 @@ video { height: 2.75rem; } -.h-12 { - height: 3rem; -} - .h-2 { height: 0.5rem; } @@ -931,14 +833,6 @@ video { height: 1px; } -.h-screen { - height: 100vh; -} - -.max-h-40 { - max-height: 10rem; -} - .max-h-96 { max-height: 24rem; } @@ -951,10 +845,6 @@ video { max-height: 100vh; } -.min-h-24 { - min-height: 6rem; -} - .min-h-\[80px\] { min-height: 80px; } @@ -971,10 +861,6 @@ video { width: 2.75rem; } -.w-12 { - width: 3rem; -} - .w-2 { width: 0.5rem; } @@ -1027,10 +913,6 @@ video { max-width: 16rem; } -.max-w-96 { - max-width: 24rem; -} - .max-w-lg { max-width: 32rem; } @@ -1039,24 +921,12 @@ video { flex: 1 1 0%; } -.shrink { - flex-shrink: 1; -} - .shrink-0 { flex-shrink: 0; } -.grow { - flex-grow: 1; -} - -.border-collapse { - border-collapse: collapse; -} - -.-translate-x-1\/2 { - --tw-translate-x: -50%; +.translate-x-3\/4 { + --tw-translate-x: 75%; transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); @@ -1076,13 +946,6 @@ video { scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } -.translate-y-0 { - --tw-translate-y: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - .translate-y-12 { --tw-translate-y: 3rem; transform: translate(var(--tw-translate-x), var(--tw-translate-y)) @@ -1121,10 +984,6 @@ video { user-select: none; } -.resize { - resize: both; -} - .flex-col { flex-direction: column; } @@ -1161,18 +1020,10 @@ video { gap: 0.375rem; } -.gap-10 { - gap: 2.5rem; -} - .gap-2 { gap: 0.5rem; } -.gap-3 { - gap: 0.75rem; -} - .gap-4 { gap: 1rem; } @@ -1213,20 +1064,10 @@ video { overflow-x: hidden; } -.truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - .whitespace-nowrap { white-space: nowrap; } -.rounded { - border-radius: 0.25rem; -} - .rounded-2xl { border-radius: 1rem; } @@ -1286,16 +1127,6 @@ video { border-top-width: 1px; } -.border-\[\#1F2428\] { - --tw-border-opacity: 1; - border-color: rgb(31 36 40 / var(--tw-border-opacity)); -} - -.border-blue-600 { - --tw-border-opacity: 1; - border-color: rgb(37 99 235 / var(--tw-border-opacity)); -} - .border-border { border-color: var(--border); } @@ -1316,6 +1147,10 @@ video { border-color: rgb(255 255 255 / 0.1); } +.border-white\/20 { + border-color: rgb(255 255 255 / 0.2); +} + .border-white\/30 { border-color: rgb(255 255 255 / 0.3); } @@ -1328,24 +1163,20 @@ video { border-top-color: transparent; } -.bg-\[\#2C3439\] { - --tw-bg-opacity: 1; - background-color: rgb(44 52 57 / var(--tw-bg-opacity)); -} - .bg-\[\#2D343A\] { --tw-bg-opacity: 1; background-color: rgb(45 52 58 / var(--tw-bg-opacity)); } -.bg-\[\#2c3439ad\] { - background-color: #2c3439ad; -} - .bg-background { background-color: var(--background); } +.bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); +} + .bg-black\/20 { background-color: rgb(0 0 0 / 0.2); } @@ -1366,11 +1197,6 @@ video { background-color: hsl(var(--destructive)); } -.bg-gray-900 { - --tw-bg-opacity: 1; - background-color: rgb(17 24 39 / var(--tw-bg-opacity)); -} - .bg-muted { background-color: hsl(var(--muted)); } @@ -1383,16 +1209,6 @@ video { background-color: var(--primary); } -.bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); -} - -.bg-red-600 { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity)); -} - .bg-secondary { background-color: var(--secondary); } @@ -1406,11 +1222,6 @@ video { background-color: transparent; } -.bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); -} - .bg-page-gradient { background-image: radial-gradient( ellipse 80% 50% at 50% -20%, @@ -1419,6 +1230,10 @@ video { ); } +.fill-current { + fill: currentColor; +} + .stroke-2 { stroke-width: 2; } @@ -1435,10 +1250,6 @@ video { padding: 0.5rem; } -.p-3 { - padding: 0.75rem; -} - .p-4 { padding: 1rem; } @@ -1451,11 +1262,6 @@ video { padding: 1px; } -.px-1 { - padding-left: 0.25rem; - padding-right: 0.25rem; -} - .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; @@ -1569,18 +1375,14 @@ video { line-height: 1.25rem; } -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - .text-xs { font-size: 0.75rem; line-height: 1rem; } -.font-extrabold { - font-weight: 800; +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; } .font-medium { @@ -1591,29 +1393,10 @@ video { font-weight: 600; } -.uppercase { - text-transform: uppercase; -} - -.lowercase { - text-transform: lowercase; -} - -.capitalize { - text-transform: capitalize; -} - .italic { font-style: italic; } -.ordinal { - --tw-ordinal: ordinal; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) - var(--tw-numeric-figure) var(--tw-numeric-spacing) - var(--tw-numeric-fraction); -} - .leading-none { line-height: 1; } @@ -1626,21 +1409,6 @@ video { letter-spacing: 0.1em; } -.text-\[\#369DFD\] { - --tw-text-opacity: 1; - color: rgb(54 157 253 / var(--tw-text-opacity)); -} - -.text-\[\#989EA4\] { - --tw-text-opacity: 1; - color: rgb(152 158 164 / var(--tw-text-opacity)); -} - -.text-\[\#B3BCC5\] { - --tw-text-opacity: 1; - color: rgb(179 188 197 / var(--tw-text-opacity)); -} - .text-card-foreground { color: hsl(var(--card-foreground)); } @@ -1657,11 +1425,6 @@ video { color: var(--foreground); } -.text-gray-900 { - --tw-text-opacity: 1; - color: rgb(17 24 39 / var(--tw-text-opacity)); -} - .text-muted-foreground { color: hsl(var(--muted-foreground)); } @@ -1674,11 +1437,6 @@ video { color: var(--primary); } -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - .text-zinc-200 { --tw-text-opacity: 1; color: rgb(228 228 231 / var(--tw-text-opacity)); @@ -1689,8 +1447,9 @@ video { color: rgb(113 113 122 / var(--tw-text-opacity)); } -.underline { - text-decoration-line: underline; +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); } .underline-offset-4 { @@ -1709,10 +1468,18 @@ video { opacity: 0.5; } +.opacity-60 { + opacity: 0.6; +} + .opacity-70 { opacity: 0.7; } +.opacity-75 { + opacity: 0.75; +} + .opacity-90 { opacity: 0.9; } @@ -1758,15 +1525,6 @@ video { outline-style: solid; } -.ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 - var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 - calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); -} - .ring-0 { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); @@ -1780,20 +1538,6 @@ video { --tw-ring-offset-color: var(--background); } -.blur { - --tw-blur: blur(8px); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) - var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) - var(--tw-sepia) var(--tw-drop-shadow); -} - -.invert { - --tw-invert: invert(100%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) - var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) - var(--tw-sepia) var(--tw-drop-shadow); -} - .filter { filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) @@ -1814,39 +1558,6 @@ video { var(--tw-backdrop-sepia); } -.\!transition { - transition-property: - color, - background-color, - border-color, - text-decoration-color, - fill, - stroke, - opacity, - box-shadow, - transform, - filter, - -webkit-backdrop-filter !important; - transition-property: color, background-color, border-color, - text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, - backdrop-filter !important; - transition-property: - color, - background-color, - border-color, - text-decoration-color, - fill, - stroke, - opacity, - box-shadow, - transform, - filter, - backdrop-filter, - -webkit-backdrop-filter !important; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important; - transition-duration: 150ms !important; -} - .transition { transition-property: color, @@ -1965,18 +1676,6 @@ video { animation-duration: 200ms; } -.running { - animation-play-state: running; -} - -.paused { - animation-play-state: paused; -} - -.\[a-zA-Z0-9\:\\\\-\\\\\._\$\] { - a-z-a--z0-9: \\-\\.$; -} - .\[background\:linear-gradient\(\#2E2E32\2c \#2E2E32\)\2c linear-gradient\(120deg\2c @@ -1987,11 +1686,147 @@ video { linear-gradient(120deg, #3f3f46, rgb(63 63 70 / 0), #3f3f46); } -#supermemory-extension-host { - position: fixed; - bottom: 0; - right: 0; - z-index: 99999; +:root { + --foreground: rgba(179, 188, 197, 1); + --foreground-menu: rgba(106, 115, 125, 1); + --background: rgba(31, 36, 40, 1); + --secondary: rgb(45, 51, 58); + --primary: rgba(54, 157, 253, 1); + --border: rgba(62, 68, 73, 1); + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary-foreground: 0 0% 98%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --radius: 0.5rem; +} + +:host { + --foreground: rgba(179, 188, 197, 1); + --foreground-menu: rgba(106, 115, 125, 1); + --background: rgba(31, 36, 40, 1); + --secondary: rgb(45, 51, 58); + --primary: rgba(54, 157, 253, 1); + --border: rgba(62, 68, 73, 1); + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary-foreground: 0 0% 98%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --radius: 0.5rem; +} + +body { + color: var(--foreground); + background: var(--background); + font-size: 14px; +} + +.gradient-background { + background: linear-gradient( + 150deg, + rgba(255, 255, 255, 0.1) 0%, + rgba(255, 255, 255, 0) + ); +} + +.no-scrollbar { + /* For WebKit (Safari, Chrome, etc.) */ + &::-webkit-scrollbar { + display: none; + } + /* For Firefox */ + scrollbar-width: none; + /* For IE and Edge */ + -ms-overflow-style: none; +} + +:not(pre) > code.hljs, +:not(pre) > code[class*="language-"] { + border-radius: 0.3em; + white-space: normal; +} + +.hljs-comment { + color: hsla(0, 0%, 100%, 0.5); +} + +.hljs-meta { + color: hsla(0, 0%, 100%, 0.6); +} + +.hljs-built_in, +.hljs-class .hljs-title { + color: #e9950c; +} + +.hljs-doctag, +.hljs-formula, +.hljs-keyword, +.hljs-literal { + color: #2e95d3; +} + +.hljs-addition, +.hljs-attribute, +.hljs-meta-string, +.hljs-regexp, +.hljs-string { + color: #00a67d; +} + +.hljs-attr, +.hljs-number, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-selector-pseudo, +.hljs-template-variable, +.hljs-type, +.hljs-variable { + color: #df3079; +} + +.hljs-bullet, +.hljs-link, +.hljs-selector-id, +.hljs-symbol, +.hljs-title { + color: #f22c3d; +} + +/* no scrollbar visibility */ + +.no-scrollbar::-webkit-scrollbar { + width: 0px; +} + +::-moz-selection { + /* Code for Firefox */ + color: #369dfd; + background: #21303d; +} + +::selection { + color: #369dfd; + background: #21303d; } .file\:border-0::file-selector-button { @@ -2027,6 +1862,13 @@ video { color: hsl(var(--muted-foreground)); } +.focus-within\:translate-x-0:focus-within { + --tw-translate-x: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) + rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) + scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .group:focus-within .group-focus-within\:block { display: block; } @@ -2208,6 +2050,10 @@ video { background-color: hsl(var(--accent)); } +.data-\[state\=open\]\:bg-secondary[data-state="open"] { + background-color: var(--secondary); +} + .data-\[state\=unchecked\]\:bg-input[data-state="unchecked"] { background-color: hsl(var(--input)); } @@ -2341,6 +2187,11 @@ video { background-color: hsl(var(--accent)); } +.hover\:bg-black:hover { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); +} + .hover\:bg-destructive\/80:hover { background-color: hsl(var(--destructive) / 0.8); } @@ -2400,6 +2251,14 @@ video { background-color: var(--foreground-menu); } +.focus\:bg-secondary:focus { + background-color: var(--secondary); +} + +.focus\:text-accent-foreground:focus { + color: hsl(var(--accent-foreground)); +} + .focus\:opacity-100:focus { opacity: 1; } @@ -2439,6 +2298,13 @@ video { --tw-ring-offset-color: #dc2626; } +.focus-visible\:translate-x-0:focus-visible { + --tw-translate-x: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) + rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) + scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .focus-visible\:outline-none:focus-visible { outline: 2px solid transparent; outline-offset: 2px; diff --git a/apps/extension/tailwind.config.js b/apps/extension/tailwind.config.js deleted file mode 100644 index 648d6d1f..00000000 --- a/apps/extension/tailwind.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const defaults = require("@repo/tailwind-config/tailwind.config"); - -/** @type {import('tailwindcss').Config} */ -module.exports = { - ...defaults, -}; diff --git a/apps/extension/tailwind.config.ts b/apps/extension/tailwind.config.ts new file mode 100644 index 00000000..cf1434cf --- /dev/null +++ b/apps/extension/tailwind.config.ts @@ -0,0 +1 @@ +module.exports = require("@repo/tailwind-config/tailwind.config"); diff --git a/apps/web/app/(auth)/auth-buttons.tsx b/apps/web/app/(auth)/auth-buttons.tsx deleted file mode 100644 index 5b0ad06e..00000000 --- a/apps/web/app/(auth)/auth-buttons.tsx +++ /dev/null @@ -1,15 +0,0 @@ -"use client"; - -import { Button } from "@repo/ui/shadcn/button"; -import React from "react"; -import { signIn } from "../../server/auth"; - -function SignIn() { - return ( - - ); -} - -export default SignIn; diff --git a/apps/web/app/(auth)/signin/page.tsx b/apps/web/app/(auth)/signin/page.tsx index bfb341ff..ba4b835a 100644 --- a/apps/web/app/(auth)/signin/page.tsx +++ b/apps/web/app/(auth)/signin/page.tsx @@ -6,15 +6,21 @@ import { Google } from "@repo/ui/components/icons"; import gradientStyle from "./_components/TextGradient/gradient.module.css"; import { cn } from "@repo/ui/lib/utils"; import { redirect } from "next/navigation"; +import { toast } from "sonner"; export const runtime = "edge"; -async function Signin() { +async function Signin({ + searchParams, +}: { + searchParams: Record; +}) { const user = await auth(); if (user) { await redirect("/home"); } + return (
diff --git a/apps/web/app/(dash)/chat/chatWindow.tsx b/apps/web/app/(dash)/chat/chatWindow.tsx index 1d54e912..7cdf7bbb 100644 --- a/apps/web/app/(dash)/chat/chatWindow.tsx +++ b/apps/web/app/(dash)/chat/chatWindow.tsx @@ -207,7 +207,7 @@ function ChatWindow({ ) : (
@@ -242,7 +242,7 @@ function ChatWindow({ {/* TODO: fade out content on the right side, the fade goes away when the user scrolls */} {/* Loading state */} diff --git a/apps/web/app/(dash)/home/page.tsx b/apps/web/app/(dash)/home/page.tsx index 32cb5d8d..27f037a8 100644 --- a/apps/web/app/(dash)/home/page.tsx +++ b/apps/web/app/(dash)/home/page.tsx @@ -36,6 +36,9 @@ function Page({ const [telegramUser, setTelegramUser] = useState( searchParams.telegramUser as string, ); + const [extensionInstalled, setExtensionInstalled] = useState< + string | undefined + >(searchParams.extension as string); const { push } = useRouter(); @@ -58,6 +61,10 @@ function Page({ linkTelegram(); } + if (extensionInstalled) { + toast.success("Extension installed successfully"); + } + getSpaces().then((res) => { if (res.success && res.data) { setSpaces(res.data); @@ -72,7 +79,7 @@ function Page({ if (typeof window === "undefined") return; window.postMessage({ token: token.data }, "*"); }); - }, []); + }, [telegramUser]); return (
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 35e6a20c..e5432796 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -18,7 +18,7 @@ "**/*.tsx", ".next/types/**/*.ts", "../../packages/ui/", - "../../packages/shared-types/utils.ts", + "../../packages/shared-types", "./components" ], "exclude": ["node_modules/"] diff --git a/package.json b/package.json index 79b1f113..f8902c5e 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@auth/drizzle-adapter": "^1.1.0", "@aws-sdk/client-s3": "^3.577.0", "@aws-sdk/s3-request-presigner": "^3.577.0", + "@babel/plugin-transform-runtime": "^7.24.7", "@cloudflare/puppeteer": "^0.0.11", "@headlessui/react": "^2.0.4", "@heroicons/react": "^2.1.4", diff --git a/packages/tailwind-config/globals.css b/packages/tailwind-config/globals.css index d4f994ed..84d5bceb 100644 --- a/packages/tailwind-config/globals.css +++ b/packages/tailwind-config/globals.css @@ -35,6 +35,39 @@ --radius: 0.5rem; } +:host { + --foreground: rgba(179, 188, 197, 1); + --foreground-menu: rgba(106, 115, 125, 1); + --background: rgba(31, 36, 40, 1); + --secondary: rgb(45, 51, 58); + --primary: rgba(54, 157, 253, 1); + --border: rgba(62, 68, 73, 1); + + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + + --primary-foreground: 0 0% 98%; + + --secondary-foreground: 0 0% 9%; + + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + + --radius: 0.5rem; +} + body { color: var(--foreground); background: var(--background); @@ -127,7 +160,6 @@ body { ); } - .no-scrollbar { /* For WebKit (Safari, Chrome, etc.) */ &::-webkit-scrollbar { diff --git a/packages/tailwind-config/tailwind.config.ts b/packages/tailwind-config/tailwind.config.ts index 8e59c5e6..69d2d9f0 100644 --- a/packages/tailwind-config/tailwind.config.ts +++ b/packages/tailwind-config/tailwind.config.ts @@ -7,9 +7,9 @@ const config = { "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}", - "../../apps/extension/**/*.{js,ts,jsx,tsx}", "../../packages/ui/**/*.{js,ts,jsx,tsx}", // Add the ui package - "../../packages/ui/shadcn/**/*.{js,ts,jsx,tsx}", // Add the ui package + "./content/**/*.{js,ts,jsx,tsx}", + "./ui/**/*.{js,ts,jsx,tsx}", ], prefix: "", theme: { From 7f76aa31354003f62fd0754fa89541ae56255c99 Mon Sep 17 00:00:00 2001 From: Dhravya Date: Thu, 4 Jul 2024 11:13:38 -0500 Subject: [PATCH 02/10] use cloudflare AI gateway --- apps/cf-ai-backend/src/helper.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/cf-ai-backend/src/helper.ts b/apps/cf-ai-backend/src/helper.ts index 44dba383..db3ab2d3 100644 --- a/apps/cf-ai-backend/src/helper.ts +++ b/apps/cf-ai-backend/src/helper.ts @@ -30,6 +30,8 @@ export async function initQuery( case "claude-3-opus": const anthropic = createAnthropic({ apiKey: c.env.ANTHROPIC_API_KEY, + baseURL: + "https://gateway.ai.cloudflare.com/v1/47c2b4d598af9d423c06fc9f936226d5/supermemory/anthropic", }); selectedModel = anthropic.chat("claude-3-opus-20240229"); console.log("Selected model: ", selectedModel); @@ -37,6 +39,8 @@ export async function initQuery( case "gemini-1.5-pro": const googleai = createGoogleGenerativeAI({ apiKey: c.env.GOOGLE_AI_API_KEY, + baseURL: + "https://gateway.ai.cloudflare.com/v1/47c2b4d598af9d423c06fc9f936226d5/supermemory/google-vertex-ai", }); selectedModel = googleai.chat("models/gemini-1.5-pro-latest"); console.log("Selected model: ", selectedModel); @@ -45,6 +49,8 @@ export async function initQuery( default: const openai = createOpenAI({ apiKey: c.env.OPENAI_API_KEY, + baseURL: + "https://gateway.ai.cloudflare.com/v1/47c2b4d598af9d423c06fc9f936226d5/supermemory/openai", }); selectedModel = openai.chat("gpt-4o"); break; From 23291be86af7b84f9eeeca475e178541510560cc Mon Sep 17 00:00:00 2001 From: Mahesh Sanikommu Date: Thu, 4 Jul 2024 10:55:56 -0700 Subject: [PATCH 03/10] Update README.md with new UI --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1fad091b..2a392e1e 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,10 @@ Just go to [supermemory.ai](https://supermemory.ai) and sign in with your google To use the chrome extension, -1. Get the chrome ext (click on the button) -2. Click on the "Extension Auth" button so the extension knows who you are :) - ![chrome](https://i.dhr.wtf/r/Clipboard_Apr_15,_2024_at_10.47 AM.png) +1. Download from [Chrome Web Store](https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnpoaffkcankadgjfc?authuser=0&hl=en-GB) +2. Now you can see on any page on bottom right (just click on it to save) + image + ## 👨‍💻 The Stack @@ -36,7 +37,8 @@ Supermemory has three main modules, managed by [turborepo](https://turbo.build): The database, auth etc logic is here -![App preview](https://i.dhr.wtf/r/Clipboard_Apr_14,_2024_at_4.10 PM.png) +![image](https://github.com/MaheshtheDev/supermemory/assets/38828053/0c44708d-600a-43a8-a641-835cb1f349fc) + Built with: @@ -54,10 +56,7 @@ Built with: The chrome extension is one of the most important part of the setup, but is not required.This is to easily add pages to your memory. -![Chrome extension preview](https://i.dhr.wtf/r/Clipboard_Apr_14,_2024_at_3.54 PM.png) - -You can also use it to import all your twitter bookmarks! -![Import bookmarks](https://i.dhr.wtf/r/Clipboard_Apr_14,_2024_at_3.56 PM.png) +image Built with: From e5ba0d8ca3180a8b3e1c7fcc31bd159389a791cf Mon Sep 17 00:00:00 2001 From: Dhravya Date: Thu, 4 Jul 2024 13:34:10 -0500 Subject: [PATCH 04/10] enter to submit, shift newline --- apps/web/app/(dash)/home/queryinput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/app/(dash)/home/queryinput.tsx b/apps/web/app/(dash)/home/queryinput.tsx index 697dc7f9..2c4b6533 100644 --- a/apps/web/app/(dash)/home/queryinput.tsx +++ b/apps/web/app/(dash)/home/queryinput.tsx @@ -80,7 +80,7 @@ function QueryInput({ className="bg-transparent pt-2.5 text-base placeholder:text-[#9B9B9B] focus:text-gray-200 duration-200 tracking-[3%] outline-none resize-none w-full p-4" placeholder="Ask your second brain..." onKeyDown={(e) => { - if (e.key === "Enter") { + if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSubmit(q, preparedSpaces); setQ(""); From ebb9dcca8e70ec8c3961fcc6a3465a788f4469f6 Mon Sep 17 00:00:00 2001 From: Dhravya Date: Thu, 4 Jul 2024 14:33:52 -0500 Subject: [PATCH 05/10] small fixes with prompt formatting and browser rendering] --- apps/cf-ai-backend/src/prompts/prompt1.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/cf-ai-backend/src/prompts/prompt1.ts b/apps/cf-ai-backend/src/prompts/prompt1.ts index 289495b6..e52fe2cd 100644 --- a/apps/cf-ai-backend/src/prompts/prompt1.ts +++ b/apps/cf-ai-backend/src/prompts/prompt1.ts @@ -19,13 +19,10 @@ export const template = ({ contexts, question }) => { const contextParts = contexts .map( ({ context, normalisedScore }) => ` - - ${context} - - - normalisedScore: ${normalisedScore} - `, + ${context ? ` ${context} ` : ""} + + ${normalisedScore ? ` normalisedScore: ${normalisedScore} ` : ""}`, ) .join("\n"); From 0da431db2bdbaf91e4512a01d97f840d7d8ef40c Mon Sep 17 00:00:00 2001 From: Dhravya Date: Thu, 4 Jul 2024 15:37:08 -0500 Subject: [PATCH 06/10] UX: Load while creating memory --- apps/browser-rendering | 2 +- apps/web/app/(dash)/menu.tsx | 9 +++++++-- apps/web/app/(landing)/Hero.tsx | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/browser-rendering b/apps/browser-rendering index 4d21045a..0135d881 160000 --- a/apps/browser-rendering +++ b/apps/browser-rendering @@ -1 +1 @@ -Subproject commit 4d21045a45fdbf56b7483d3704ae0474ebf044fb +Subproject commit 0135d8813311b6825382f411cda13f5663b6c462 diff --git a/apps/web/app/(dash)/menu.tsx b/apps/web/app/(dash)/menu.tsx index 11738391..6cea35a4 100644 --- a/apps/web/app/(dash)/menu.tsx +++ b/apps/web/app/(dash)/menu.tsx @@ -109,7 +109,10 @@ function Menu() { const handleSubmit = async (content?: string, space?: string) => { setDialogOpen(false); - toast.info("Creating memory..."); + toast.info("Creating memory...", { + icon: , + duration: 7500, + }); if (!content || content.length === 0) { toast.error("Content is required"); @@ -124,7 +127,9 @@ function Menu() { setContent(""); if (cont.success) { - toast.success("Memory created"); + toast.success("Memory created", { + richColors: true, + }); } else { toast.error(`Memory creation failed: ${cont.error}`); } diff --git a/apps/web/app/(landing)/Hero.tsx b/apps/web/app/(landing)/Hero.tsx index 4a0529aa..5565de3a 100644 --- a/apps/web/app/(landing)/Hero.tsx +++ b/apps/web/app/(landing)/Hero.tsx @@ -75,7 +75,7 @@ function Hero() { width={1512} height={1405} draggable="false" - className="z-40 md:mt-[-40px] hidden sm:block h-full max-w-[70vw] mx-auto md:w-full select-none px-5 rounded-2xl" + className="z-40 md:mt-[-40px] hidden sm:block h-full max-w-[70vw] mx-auto md:w-full select-none px-5 !rounded-2xl" style={{ borderRadius: "20px", }} From 6ef746da542b8e5da3ef638fb7f46f5aa10a3a02 Mon Sep 17 00:00:00 2001 From: Dhravya Date: Thu, 4 Jul 2024 19:17:51 -0500 Subject: [PATCH 07/10] revamped extention --- apps/cf-ai-backend/src/helper.ts | 1 + apps/cf-ai-backend/src/index.ts | 2 +- .../cf-ai-backend/src/utils/OpenAIEmbedder.ts | 23 +- apps/extension/background.ts | 72 ++- apps/extension/content/ContentApp.tsx | 365 +++++++++++--- apps/extension/content/content.tsx | 2 +- apps/extension/content/ui/shadcn/input.tsx | 25 + apps/extension/content/ui/shadcn/label.tsx | 26 + apps/extension/content/ui/shadcn/popover.tsx | 2 +- apps/extension/content/ui/shadcn/select.tsx | 162 ++++++ apps/extension/content/ui/shadcn/textarea.tsx | 24 + apps/extension/content/ui/shadcn/tooltip.tsx | 2 +- apps/extension/content/ui/shadcn/use-toast.ts | 2 +- apps/extension/package.json | 2 + apps/extension/public/output.css | 466 ++++++++---------- apps/web/app/api/ensureAuth.ts | 2 +- apps/web/app/api/store/route.ts | 16 +- apps/web/middleware.ts | 27 +- 18 files changed, 840 insertions(+), 381 deletions(-) create mode 100644 apps/extension/content/ui/shadcn/input.tsx create mode 100644 apps/extension/content/ui/shadcn/label.tsx create mode 100644 apps/extension/content/ui/shadcn/select.tsx create mode 100644 apps/extension/content/ui/shadcn/textarea.tsx diff --git a/apps/cf-ai-backend/src/helper.ts b/apps/cf-ai-backend/src/helper.ts index db3ab2d3..98e38ce8 100644 --- a/apps/cf-ai-backend/src/helper.ts +++ b/apps/cf-ai-backend/src/helper.ts @@ -51,6 +51,7 @@ export async function initQuery( apiKey: c.env.OPENAI_API_KEY, baseURL: "https://gateway.ai.cloudflare.com/v1/47c2b4d598af9d423c06fc9f936226d5/supermemory/openai", + compatibility: "strict", }); selectedModel = openai.chat("gpt-4o"); break; diff --git a/apps/cf-ai-backend/src/index.ts b/apps/cf-ai-backend/src/index.ts index 31ee520f..04f80f0d 100644 --- a/apps/cf-ai-backend/src/index.ts +++ b/apps/cf-ai-backend/src/index.ts @@ -509,7 +509,7 @@ app.post( const userMessage: CoreMessage = { role: "user", content: prompt }; const response = await streamText({ - model, + model: model, messages: [ ...initialMessages, ...((body.chatHistory || []) as CoreMessage[]), diff --git a/apps/cf-ai-backend/src/utils/OpenAIEmbedder.ts b/apps/cf-ai-backend/src/utils/OpenAIEmbedder.ts index be5839b1..8364ed0d 100644 --- a/apps/cf-ai-backend/src/utils/OpenAIEmbedder.ts +++ b/apps/cf-ai-backend/src/utils/OpenAIEmbedder.ts @@ -22,17 +22,20 @@ export class OpenAIEmbeddings { } async embedQuery(text: string): Promise { - const response = await fetch("https://api.openai.com/v1/embeddings", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${this.apiKey}`, + const response = await fetch( + "https://gateway.ai.cloudflare.com/v1/47c2b4d598af9d423c06fc9f936226d5/supermemory/openai/embeddings", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${this.apiKey}`, + }, + body: JSON.stringify({ + input: text, + model: this.modelName, + }), }, - body: JSON.stringify({ - input: text, - model: this.modelName, - }), - }); + ); const data = await response.json(); diff --git a/apps/extension/background.ts b/apps/extension/background.ts index 97bb341c..df4d0375 100644 --- a/apps/extension/background.ts +++ b/apps/extension/background.ts @@ -74,15 +74,28 @@ const batchImportAll = async (cursor = "", totalImported = 0) => { description: tweet.text.slice(0, 200), type: "tweet", }), - }).then((ers) => { + }).then(async (ers) => { console.log(ers.status); importedCount++; totalImported++; - // Send an update message to the content script - chrome.runtime.sendMessage({ - type: "import-update", - importedCount: totalImported, - }); + console.log(totalImported); + chrome.tabs.query( + { active: true, currentWindow: true }, + async function (tabs) { + if (tabs.length > 0) { + let currentTabId = tabs[0].id; + + if (!currentTabId) { + return; + } + + await chrome.tabs.sendMessage(currentTabId, { + type: "import-update", + importedCount: totalImported, + }); + } + }, + ); }); }); })(); @@ -111,19 +124,45 @@ const batchImportAll = async (cursor = "", totalImported = 0) => { batchImportAll(nextCursor, totalImported); // Recursively call with new cursor } else { console.log("All bookmarks imported"); - // Send a "done" message to the content script - chrome.runtime.sendMessage({ - type: "import-done", - importedCount: totalImported, - }); + + chrome.tabs.query( + { active: true, currentWindow: true }, + async function (tabs) { + if (tabs.length > 0) { + let currentTabId = tabs[0].id; + + if (!currentTabId) { + return; + } + + await chrome.runtime.sendMessage({ + type: "import-done", + importedCount: totalImported, + }); + } + }, + ); } } else { console.log("All bookmarks imported"); // Send a "done" message to the content script - chrome.runtime.sendMessage({ - type: "import-done", - importedCount: totalImported, - }); + chrome.tabs.query( + { active: true, currentWindow: true }, + async function (tabs) { + if (tabs.length > 0) { + let currentTabId = tabs[0].id; + + if (!currentTabId) { + return; + } + + await chrome.runtime.sendMessage({ + type: "import-done", + importedCount: totalImported, + }); + } + }, + ); } }) .catch((error) => console.error(error)); @@ -311,10 +350,9 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { }).then((ers) => console.log(ers.status)); }); })(); - - return true; } else if (request.type === "batchImportAll") { batchImportAll(); + return true; } }); diff --git a/apps/extension/content/ContentApp.tsx b/apps/extension/content/ContentApp.tsx index c1af1dfc..1dea7645 100644 --- a/apps/extension/content/ContentApp.tsx +++ b/apps/extension/content/ContentApp.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; import { Readability } from "@mozilla/readability"; -import tailwindBg from "../public/tailwind_bg.png"; import { Tooltip, TooltipContent, @@ -9,7 +8,19 @@ import { } 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"; const BACKEND_URL = "https://supermemory.ai"; @@ -20,19 +31,70 @@ export default function ContentApp({ token: string | undefined; shadowRoot: ShadowRoot; }) { - const [text, setText] = useState(""); const [hover, setHover] = useState(false); + const { toast } = useToast(); + const [loading, setLoading] = useState(false); + const [webNote, setWebNote] = useState(""); + const [importedCount, setImportedCount] = useState(0); const [isImporting, setIsImporting] = useState(false); + const [importDone, setImportDone] = useState(false); const [portalContainer, setPortalContainer] = useState( null, ); const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const { toast } = useToast(); + 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: ( +
+ + + +
+ ), + }); + } + }; useEffect(() => { document.addEventListener("mousemove", (e) => { @@ -47,24 +109,21 @@ export default function ContentApp({ }); const getUserData = () => { - chrome.runtime.sendMessage({ type: "getJwt" }, (response) => { - console.log("jwt", response.jwt); - }); + chrome.runtime.sendMessage({ type: "getJwt" }); }; getUserData(); chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { - console.log("request", request); - console.log("sender", sender); - console.log("sendResponse", sendResponse); if (request.type === "import-update") { + console.log(request); setIsImporting(true); setImportedCount(request.importedCount); } if (request.type === "import-done") { setIsImporting(false); + setImportDone(true); } }); @@ -73,12 +132,36 @@ export default function ContentApp({ shadowRoot.appendChild(portalDiv); setPortalContainer(portalDiv); + 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`, + ); + } + }; + + getSpaces(); + return () => { document.removeEventListener("mousemove", () => {}); }; - }, [document.getElementById("supermemory-extension-host")]); + }, []); - function sendUrlToAPI(spaces: number[]) { + async function sendUrlToAPI(spaces: string[]) { setLoading(true); setTimeout(() => { @@ -91,7 +174,6 @@ export default function ContentApp({ const blacklist: string[] = []; // check if the URL is blacklisted if (blacklist.some((blacklisted) => url.includes(blacklisted))) { - console.log("URL is blacklisted"); return; } else { const clone = document.cloneNode(true) as Document; @@ -105,16 +187,45 @@ export default function ContentApp({ document.querySelector('link[rel="icon"]') as HTMLLinkElement )?.href; - console.log("article", article); - chrome.runtime.sendMessage({ - type: "urlSave", - content: article?.textContent, - url, - spaces, - title: article?.title, - description: article?.excerpt, - ogImage: ogImage, - favicon: favicon, + 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", + }); + } else { + toast({ + title: `Failed to save to supermemory.ai: ${d.error ?? "Unknown error"}`, + }); + } + setLoading(false); + return rep; }); } } @@ -134,69 +245,179 @@ export default function ContentApp({ - {loading ? ( -
Saving...
- ) : ( - - - - )} + + +
-

Add to supermemory.ai

+ {userNotLoggedIn ? ( + <>You need to login to use this extension. + ) : ( +

Add to supermemory.ai

+ )}
- -
-

Select a space

+ + {userNotLoggedIn ? ( +
+ +
+ ) : ( +
+ - -
+ +