+
+
-
- Add memory
-
- A "Memory" is a bookmark, something you want to remember.
-
-
+
+
Home
+
-
-
-
-
-
-
-
-
({
- label: x.name,
- value: x.id.toString(),
- }))}
- onSelect={(v) =>
- setSelectedSpaces((prev) => {
- if (v === "") {
- return [];
- }
- return [...prev, parseInt(v)];
- })
- }
- onSubmit={async (spaceName) => {
- const space = options.find((x) => x.label === spaceName);
- toast.info("Creating space...");
-
- if (space) {
- toast.error("A space with that name already exists.");
- }
-
- const creationTask = await createSpace(spaceName);
- if (creationTask.success && creationTask.data) {
- toast.success("Space created " + creationTask.data);
- setSpaces((prev) => [
- ...prev,
- {
- name: spaceName,
- id: creationTask.data!,
- createdAt: new Date(),
- user: null,
- numItems: 0,
- },
- ]);
- setSelectedSpaces((prev) => [...prev, creationTask.data!]);
- } else {
- toast.error(
- "Space creation failed: " + creationTask.error ??
- "Unknown error",
- );
- }
- }}
- placeholder="Select or create a new space."
- className="bg-[#2F353C] h-min rounded-md mt-4 mb-4"
- />
-
-
- {selectedSpaces.length > 0 && (
-
- {[...new Set(selectedSpaces)].map((x, idx) => (
-
- ))}
-
- )}
-
-
-
-
-
-
-
-
-
- {/* Mobile Menu */}
-
-
+
+ {menuItems.map((item) => (
-
-
Home
-
-
-
- Add
-
- {menuItems.map((item) => (
-
item.disabled && e.preventDefault()}
- >
-
-
{item.text}
-
- ))}
-
+
{item.text}
+
+ ))}
-
+
>
);
}
+export function Navbar() {
+ return (
+
+
+
+
+
+ {items.map((v) => (
+
+ ))}
+
+
+
+ );
+}
+
+function Top() {
+ return (
+
+
+
+
+
+
+ Add Memories
+
+
+
+
+ );
+}
+
+function NavItem({
+ disabled,
+ icon,
+ url,
+ name,
+}: {
+ disabled: boolean;
+ icon: React.JSX.Element;
+ name: string;
+ url: string;
+}) {
+ return (
+
+
+
+ {icon}
+
+
+
+ {name}
+
+
+ );
+}
+
export default Menu;
diff --git a/apps/web/app/(onboarding)/layout.tsx b/apps/web/app/(onboarding)/layout.tsx
new file mode 100644
index 00000000..10a9a3ec
--- /dev/null
+++ b/apps/web/app/(onboarding)/layout.tsx
@@ -0,0 +1,12 @@
+import React from "react";
+import { Toaster } from "@repo/ui/shadcn/sonner";
+
+function layout({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+export default layout;
diff --git a/apps/web/app/(onboarding)/onboarding/page.tsx b/apps/web/app/(onboarding)/onboarding/page.tsx
new file mode 100644
index 00000000..294efb33
--- /dev/null
+++ b/apps/web/app/(onboarding)/onboarding/page.tsx
@@ -0,0 +1,388 @@
+"use client";
+
+import Link from "next/link";
+import {
+ ChevronLeftIcon,
+ ChevronRightIcon,
+ QuestionMarkCircleIcon,
+ ArrowTurnDownLeftIcon,
+} from "@heroicons/react/24/solid";
+import { CheckIcon, PlusCircleIcon } from "@heroicons/react/24/outline";
+import { motion } from "framer-motion";
+import { useState } from "react";
+import { toast } from "sonner";
+import { createMemory } from "@/app/actions/doers";
+import { useRouter } from "next/navigation";
+
+export default function Home() {
+ const [currStep, setCurrStep] = useState(0);
+
+ return (
+
+ {/* Navbar */}
+
+
+ {/* main-content */}
+
+ {currStep === 0 && (
+
+
+ We are so excited to have you.. but but first let's set up
+ everything
+
+
+ )}
+ {currStep >= 1 &&
}
+ {currStep >= 2 &&
}
+ {currStep >= 3 &&
}
+
+
+ setCurrStep(v)}
+ currStep={currStep}
+ />
+
+
+ );
+}
+
+function StepOne({ currStep }: { currStep: number }) {
+ return (
+
+ 1 ? (currStep > 2 ? -40 : -20) : 0,
+ }}
+ transition={{ duration: 0.2, type: "spring", bounce: 0.1 }}
+ >
+ 1
+ ? "bg-[#26D987]/10 text-[#26D987]"
+ : "bg-[#1F2428] text-white"
+ } `}
+ >
+
+
+
+
Import twitter bookmarks
+
+ Directly import all your bookmarks from twitter in single click
+
+
+
+
{getStatusIcon({ completed: currStep > 1 })}
+
+ {currStep === 1 && (
+
+
+ -
+ First Download our Extension from{" "}
+
+ here
+
+
+ -
+ Once downloaded, it will try to authenticate you by visiting
+ supermemory website, coordinate it with it.
+
+ -
+ After successful authentication, visit x.com and click on the
+ bookmark icon and done, all your bookmarks are in supermemory.
+
+ -
+
+ hover over the bottom left area of the browser window to see
+ the extension
+
+
+
+

+
+ )}
+
+
+ );
+}
+
+function StepIndicator({
+ currStep,
+ setCurrStep,
+}: {
+ currStep: number;
+ setCurrStep: (v: number) => void;
+}) {
+ return (
+
+
+
= 2 ? "opacity-100" : "opacity-0"}`}
+ onClick={() => currStep >= 2 && setCurrStep(currStep - 1)}
+ />
+ Step: {currStep}/3
+ currStep <= 3 && setCurrStep(currStep + 1)}
+ />
+
+
+
+ {Array.from({ length: 3 }).map((_, i) => (
+
i + 1 ? "bg-[#26D987]" : "bg-white/10"
+ } rounded-full overflow-hidden`}
+ >
+ {i === currStep - 1 && (
+
+ )}
+
+ ))}
+
+
+ );
+}
+
+function StepThree({ currStep }: { currStep: number }) {
+ const { push } = useRouter();
+
+ return (
+
+ 3
+ ? "bg-[#26D987]/10 text-[#26D987]"
+ : "bg-[#1F2428] text-white"
+ } `}
+ >
+ {/* info */}
+
+ {/* icon */}
+
+ {/* custom twitter icon */}
+
+
+
+
Ask a question
+
+ Let's use the true power of supermemory!
+
+
+
+
+ {/* status */}
+
{getStatusIcon({ completed: currStep > 3 })}
+
+
+ {currStep === 3 && (
+
+
+ -
+ Let's start off by adding some content! We have added
+ supermemory's link for you press enter
+
+ -
+
+ Once saved, you'll be redirected to home from there you can
+ search supermemory.ai or whatever you saved, and woilla, the
+ result is there!
+
+
+ -
+
+
+
+
+ )}
+
+ );
+}
+
+function StepTwo({ currStep }: { currStep: number }) {
+ return (
+
+ 2 ? -20 : 0 }}
+ transition={{ duration: 0.2, type: "spring", bounce: 0 }}
+ >
+ 2
+ ? "bg-[#26D987]/10 text-[#26D987]"
+ : "bg-[#1F2428] text-white"
+ } `}
+ >
+ {/* info */}
+
+ {/* icon */}
+
+ {/* custom twitter icon */}
+
+
+
+
Adding Content into supermemory
+
one click method to save your time.
+
+
+
+ {/* status */}
+
{getStatusIcon({ completed: currStep > 2 })}
+
+
+ {currStep === 2 && (
+
+
+ -
+ You can either add content from the home or from the extension.
+
+
+

+

+
+ )}
+
+
+ );
+}
+
+function Navbar() {
+ return (
+
+ {/* logo */}
+
+
+
+
+
+
+ );
+}
+
+const getStatusIcon = ({ completed }: { completed: boolean }) => {
+ if (!completed) {
+ return (
+
+ );
+ } else {
+ return
;
+ }
+};
diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts
index 1838ee1c..74df0b04 100644
--- a/apps/web/app/actions/fetchers.ts
+++ b/apps/web/app/actions/fetchers.ts
@@ -1,6 +1,6 @@
"use server";
-import { and, asc, eq, exists, inArray, not, or, sql } from "drizzle-orm";
+import { and, asc, desc, eq, exists, inArray, not, or, sql } from "drizzle-orm";
import { db } from "../../server/db";
import {
canvas,
@@ -228,6 +228,34 @@ export const getFullChatThread = async (
};
};
+export const getRecentChats = async (): ServerActionReturnType
=> {
+ const data = await auth();
+
+ if (!data || !data.user || !data.user.id) {
+ redirect("/signin");
+ return { error: "Not authenticated", success: false };
+ }
+
+ try {
+ const chatHistorys = await db.query.chatThreads.findMany({
+ where: eq(chatThreads.userId, data.user.id),
+ orderBy: desc(chatThreads.createdAt),
+ limit: 4,
+ });
+
+ return {
+ success: true,
+ data: chatHistorys,
+ };
+ } catch (e) {
+ return {
+ success: false,
+ error: (e as Error).message,
+ };
+ }
+};
+
+
export const getChatHistory = async (): ServerActionReturnType<
ChatThread[]
> => {
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
index cf6e9b0f..2d612eef 100644
--- a/apps/web/app/layout.tsx
+++ b/apps/web/app/layout.tsx
@@ -1,13 +1,13 @@
import "@repo/tailwind-config/globals.css";
import type { Metadata } from "next";
-import { Inter } from "next/font/google";
+import { Poppins } from "next/font/google";
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
import { cn } from "@repo/ui/lib/utils";
import { Toaster } from "@repo/ui/shadcn/toaster";
-const inter = Inter({ subsets: ["latin"] });
+const poppins = Poppins({ subsets: ["latin"], weight: ["400", "500", "600", "700"] });
export const runtime = "edge";
@@ -72,7 +72,7 @@ export default function RootLayout({
{/* TODO: when lightmode support is added, remove the 'dark' class from the body tag */}
users.id, { onDelete: "cascade" }),
- },
+ createdAt: integer("createdAt").notNull().$defaultFn(() => Math.floor(Date.now() / 1000)), // Use Unix timestamp
+ },
(thread) => ({
userIdx: index("chatThread_user_idx").on(thread.userId),
}),
diff --git a/packages/tailwind-config/globals.css b/packages/tailwind-config/globals.css
index dc3d3ed8..ffce0b11 100644
--- a/packages/tailwind-config/globals.css
+++ b/packages/tailwind-config/globals.css
@@ -234,3 +234,10 @@ body {
color: #369dfd;
background: #21303d;
}
+
+.bg {
+ background-color: #171B1F;
+ opacity: 1;
+ background: radial-gradient(circle, transparent 20%, #171B1F 20%, #171B1F 80%, transparent 80%, transparent), radial-gradient(circle, transparent 20%, #171B1F 20%, #171B1F 80%, transparent 80%, transparent) 100px 100px, linear-gradient(#1a1f23 8px, transparent 8px) 0 -4px, linear-gradient(90deg, #1a1f23 8px, #171B1F 8px) -4px 0;
+ background-size: 100px 100px, 100px 100px, 100px 100px, 100px 100px;
+}
\ No newline at end of file
diff --git a/packages/ui/shadcn/command.tsx b/packages/ui/shadcn/command.tsx
index ed929aa2..9b95f4c7 100644
--- a/packages/ui/shadcn/command.tsx
+++ b/packages/ui/shadcn/command.tsx
@@ -37,9 +37,8 @@ const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
const CommandInput = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-
+>(({ className , ...props }, ref) => (
+
;
-}
-
-export default Divider;
diff --git a/packages/ui/shadcn/skeleton.tsx b/packages/ui/shadcn/skeleton.tsx
new file mode 100644
index 00000000..001c280d
--- /dev/null
+++ b/packages/ui/shadcn/skeleton.tsx
@@ -0,0 +1,15 @@
+import { cn } from "../lib/utils"
+
+function Skeleton({
+ className,
+ ...props
+}: React.HTMLAttributes
) {
+ return (
+
+ )
+}
+
+export { Skeleton }