diff --git a/apps/web/app/(auth)/signin/_components/TextGradient/gradient.module.css b/apps/web/app/(auth)/signin/_components/TextGradient/gradient.module.css new file mode 100644 index 00000000..b9afc7d1 --- /dev/null +++ b/apps/web/app/(auth)/signin/_components/TextGradient/gradient.module.css @@ -0,0 +1,70 @@ +@keyframes background-pan { + from { + background-position: 0% center; + } + + to { + background-position: -200% center; + } + } + + @keyframes scale { + from, + to { + transform: scale(0); + } + + 50% { + transform: scale(1); + } + } + + @keyframes rotate { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(180deg); + } + } + + .star { + --size: clamp(20px, 1.5vw, 30px); + + animation: scale 700ms ease forwards; + display: block; + height: var(--size); + left: var(--star-left); + position: absolute; + top: var(--star-top); + width: var(--size); + } + + .star > svg { + animation: rotate 1000ms linear infinite; + display: block; + opacity: 0.7; + } + + .star > svg > path { + fill: #7b3907; + } + + .magicText { + --purple: rgba(72, 130, 244, 0.868); + --violet: #9a80f7; + --pink: #f7f7f7; + animation: background-pan 3s linear infinite; + background: linear-gradient( + to right, + var(--purple), + var(--violet), + var(--pink), + var(--purple) + ); + background-size: 200%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + white-space: nowrap; + } \ No newline at end of file diff --git a/apps/web/app/(auth)/signin/page.tsx b/apps/web/app/(auth)/signin/page.tsx index 48074bf4..b117716e 100644 --- a/apps/web/app/(auth)/signin/page.tsx +++ b/apps/web/app/(auth)/signin/page.tsx @@ -3,81 +3,100 @@ import Link from "next/link"; import Logo from "@/public/logo.svg"; import { signIn } from "@/server/auth"; import { Google } from "@repo/ui/components/icons"; +import gradientStyle from "./_components/TextGradient/gradient.module.css"; +import { cn } from "@repo/ui/lib/utils"; export const runtime = "edge"; -async function Signin({ - searchParams, -}: { - searchParams: Record; -}) { - const searchParamsAsString = Object.keys(searchParams) - .map((key) => { - return `${key}=${searchParams[key]}`; - }) - .join("&"); - +async function Signin() { return ( -
-
+
+
- SuperMemory logo - SuperMemory.ai + SuperMemory logo + supermemory.ai
+
+ back bg -
-

- Hello, human +
+

+ Hello,{" "} + + human + {" "}

-

+

Write, ideate, and learn with all the wisdom of your bookmarks.

{ "use server"; await signIn("google", { - redirectTo: `/home?firstTime=true&${searchParamsAsString}`, + redirectTo: "/home?firstTime=true", }); }} >
-
- +
+ By continuing, you agree to the + + {" "} Terms of Service {" "} - | - + and + {" "} Privacy Policy
-
- +
+ back bg + Ready for your{" "} Second brain?
Carousel 01; // Container ref + fromRef: RefObject; + toRef: RefObject; + curvature?: number; + reverse?: boolean; + pathColor?: string; + pathWidth?: number; + pathOpacity?: number; + gradientStartColor?: string; + gradientStopColor?: string; + delay?: number; + duration?: number; + startXOffset?: number; + startYOffset?: number; + endXOffset?: number; + endYOffset?: number; +} + +export const AnimatedBeam: React.FC = ({ + className, + containerRef, + fromRef, + toRef, + curvature = 0, + reverse = false, // Include the reverse prop + duration = Math.random() * 3 + 4, + delay = 0, + pathColor = "gray", + pathWidth = 2, + pathOpacity = 0.2, + gradientStartColor = "#ffaa40", + gradientStopColor = "#9c40ff", + startXOffset = 0, + startYOffset = 0, + endXOffset = 0, + endYOffset = 0, +}) => { + const id = useId(); + const [pathD, setPathD] = useState(""); + const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 }); + + // Calculate the gradient coordinates based on the reverse prop + const gradientCoordinates = reverse + ? { + x1: ["90%", "-10%"], + x2: ["100%", "0%"], + y1: ["0%", "0%"], + y2: ["0%", "0%"], + } + : { + x1: ["10%", "110%"], + x2: ["0%", "100%"], + y1: ["0%", "0%"], + y2: ["0%", "0%"], + }; + + useEffect(() => { + const updatePath = () => { + if (containerRef.current && fromRef.current && toRef.current) { + const containerRect = containerRef.current.getBoundingClientRect(); + const rectA = fromRef.current.getBoundingClientRect(); + const rectB = toRef.current.getBoundingClientRect(); + + const svgWidth = containerRect.width; + const svgHeight = containerRect.height; + setSvgDimensions({ width: svgWidth, height: svgHeight }); + + const startX = + rectA.left - containerRect.left + rectA.width / 2 + startXOffset; + const startY = + rectA.top - containerRect.top + rectA.height / 2 + startYOffset; + const endX = + rectB.left - containerRect.left + rectB.width / 2 + endXOffset; + const endY = + rectB.top - containerRect.top + rectB.height / 2 + endYOffset; + + const controlY = startY - curvature; + const d = `M ${startX},${startY} Q ${(startX + endX) / 2 + },${controlY} ${endX},${endY}`; + setPathD(d); + } + }; + + // Initialize ResizeObserver + const resizeObserver = new ResizeObserver((entries) => { + // For all entries, recalculate the path + for (let entry of entries) { + updatePath(); + } + }); + + // Observe the container element + if (containerRef.current) { + resizeObserver.observe(containerRef.current); + } + + // Call the updatePath initially to set the initial path + updatePath(); + + // Clean up the observer on component unmount + return () => { + resizeObserver.disconnect(); + }; + }, [ + containerRef, + fromRef, + toRef, + curvature, + startXOffset, + startYOffset, + endXOffset, + endYOffset, + ]); + + return ( + + + + + + + + + + + + + ); +}; diff --git a/apps/web/app/(landing)/CardPatterns/AnimatedBeamWithOutput.tsx b/apps/web/app/(landing)/CardPatterns/AnimatedBeamWithOutput.tsx new file mode 100644 index 00000000..94774ec2 --- /dev/null +++ b/apps/web/app/(landing)/CardPatterns/AnimatedBeamWithOutput.tsx @@ -0,0 +1,494 @@ +"use client"; +import { cn } from "@repo/ui/lib/utils"; +import { AnimatedBeam } from "./AnimatedBeam"; +import React, { forwardRef, useRef } from "react"; +import Logo from "../../../public/logo.svg"; +import Image from "next/image"; +const Circle = forwardRef< + HTMLDivElement, + { className?: string; children?: React.ReactNode } +>(({ className, children }, ref) => { + return ( +
+ {children} +
+ ); +}); + +export function AnimatedBeamShow() { + const containerRef = useRef(null); + const div1Ref = useRef(null); + const div2Ref = useRef(null); + const div3Ref = useRef(null); + const div4Ref = useRef(null); + const div5Ref = useRef(null); + const div6Ref = useRef(null); + const div7Ref = useRef(null); + + return ( +
+
+
+ + + + + + +
+
+ + + + + logo image + + + + +
+
+ + + + + + +
+
+ + + + + + + +
+ ); +} + +const Icons = { + notion: () => ( + + + + + ), + openai: () => ( + + + + ), + googleDrive: () => ( + + + + + + + + + ), + whatsapp: () => ( + + + + + + + + + + + + + + + + + ), + googleDocs: () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ), + zapier: () => ( + + + + + + + + + + + ), + messenger: () => ( + + + + + + + + + + + + + + + + ), +}; diff --git a/apps/web/app/(landing)/CardPatterns/AnimatedGrid.tsx b/apps/web/app/(landing)/CardPatterns/AnimatedGrid.tsx new file mode 100644 index 00000000..31f9e52c --- /dev/null +++ b/apps/web/app/(landing)/CardPatterns/AnimatedGrid.tsx @@ -0,0 +1,149 @@ +"use client"; + +import { cn } from "@repo/ui/lib/utils"; +import { motion } from "framer-motion"; +import { useEffect, useId, useRef, useState } from "react"; + +interface GridPatternProps { + width?: number; + height?: number; + x?: number; + y?: number; + strokeDasharray?: any; + numSquares?: number; + className?: string; + maxOpacity?: number; + duration?: number; + repeatDelay?: number; +} + +export function GridPattern({ + width = 40, + height = 40, + x = -1, + y = -1, + strokeDasharray = 0, + numSquares = 50, + className, + maxOpacity = 0.5, + duration = 4, + repeatDelay = 0.5, + ...props +}: GridPatternProps) { + const id = useId(); + const containerRef = useRef(null); + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); + const [squares, setSquares] = useState(() => generateSquares(numSquares)); + + function getPos() { + return [ + Math.floor((Math.random() * dimensions.width) / width), + Math.floor((Math.random() * dimensions.height) / height), + ]; + } + + // Adjust the generateSquares function to return objects with an id, x, and y + function generateSquares(count: number) { + return Array.from({ length: count }, (_, i) => ({ + id: i, + pos: getPos(), + })); + } + + // Function to update a single square's position + const updateSquarePosition = (id: number) => { + setSquares((currentSquares) => + currentSquares.map((sq) => + sq.id === id + ? { + ...sq, + pos: getPos(), + } + : sq, + ), + ); + }; + + // Update squares to animate in + useEffect(() => { + if (dimensions.width && dimensions.height) { + setSquares(generateSquares(numSquares)); + } + }, [dimensions, numSquares]); + + // Resize observer to update container dimensions + useEffect(() => { + const resizeObserver = new ResizeObserver((entries) => { + for (let entry of entries) { + setDimensions({ + width: entry.contentRect.width, + height: entry.contentRect.height, + }); + } + }); + + if (containerRef.current) { + resizeObserver.observe(containerRef.current); + } + + return () => { + if (containerRef.current) { + resizeObserver.unobserve(containerRef.current); + } + }; + }, [containerRef]); + + return ( + + ); +} + +export default GridPattern; diff --git a/apps/web/app/(landing)/CardPatterns/Glare.tsx b/apps/web/app/(landing)/CardPatterns/Glare.tsx new file mode 100644 index 00000000..53550ecc --- /dev/null +++ b/apps/web/app/(landing)/CardPatterns/Glare.tsx @@ -0,0 +1,139 @@ +"use client"; + +import { cn } from "@repo/ui/lib/utils"; +import { useRef } from "react"; + +export const GlareCard = ({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) => { + const isPointerInside = useRef(false); + const refElement = useRef(null); + const state = useRef({ + glare: { + x: 50, + y: 50, + }, + background: { + x: 50, + y: 50, + }, + rotate: { + x: 0, + y: 0, + }, + }); + const containerStyle = { + "--m-x": "50%", + "--m-y": "50%", + "--r-x": "0deg", + "--r-y": "0deg", + "--bg-x": "50%", + "--bg-y": "50%", + "--duration": "300ms", + "--foil-size": "100%", + "--opacity": "0", + "--radius": "23px", + "--easing": "ease", + "--transition": "var(--duration) var(--easing)", + } as any; + + const backgroundStyle = { + "--step": "5%", + "--foil-svg": `url("data:image/svg+xml,%3Csvg width='26' height='26' viewBox='0 0 26 26' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.99994 3.419C2.99994 3.419 21.6142 7.43646 22.7921 12.153C23.97 16.8695 3.41838 23.0306 3.41838 23.0306' stroke='white' stroke-width='5' stroke-miterlimit='3.86874' stroke-linecap='round' style='mix-blend-mode:darken'/%3E%3C/svg%3E")`, + "--pattern": "var(--foil-svg) center/100% no-repeat", + "--rainbow": + "repeating-linear-gradient( 0deg,rgb(255,119,115) calc(var(--step) * 1),rgba(255,237,95,1) calc(var(--step) * 2),rgba(168,255,95,1) calc(var(--step) * 3),rgba(131,255,247,1) calc(var(--step) * 4),rgba(120,148,255,1) calc(var(--step) * 5),rgb(216,117,255) calc(var(--step) * 6),rgb(255,119,115) calc(var(--step) * 7) ) 0% var(--bg-y)/200% 700% no-repeat", + "--diagonal": + "repeating-linear-gradient( 128deg,#0e152e 0%,hsl(180,10%,60%) 3.8%,hsl(180,10%,60%) 4.5%,hsl(180,10%,60%) 5.2%,#0e152e 10%,#0e152e 12% ) var(--bg-x) var(--bg-y)/300% no-repeat", + "--shade": + "radial-gradient( farthest-corner circle at var(--m-x) var(--m-y),rgba(255,255,255,0.1) 12%,rgba(255,255,255,0.15) 20%,rgba(255,255,255,0.25) 120% ) var(--bg-x) var(--bg-y)/300% no-repeat", + "--hero-gradient": + 'radial-gradient(ellipse 50% 80% at 20% 40%, rgba(93, 52, 221, 0.1), transparent), radial-gradient(ellipse 50% 80% at 80% 50%, rgba(120, 119, 198, 0.15),transparent)', + + backgroundBlendMode: "hue, hue, hue, overlay", + }; + + const updateStyles = () => { + if (refElement.current) { + console.log(state.current); + const { background, rotate, glare } = state.current; + refElement.current?.style.setProperty("--m-x", `${glare.x}%`); + refElement.current?.style.setProperty("--m-y", `${glare.y}%`); + refElement.current?.style.setProperty("--r-x", `${rotate.x}deg`); + refElement.current?.style.setProperty("--r-y", `${rotate.y}deg`); + refElement.current?.style.setProperty("--bg-x", `${background.x}%`); + refElement.current?.style.setProperty("--bg-y", `${background.y}%`); + } + }; + return ( +
{ + const rotateFactor = 0.4; + const rect = event.currentTarget.getBoundingClientRect(); + const position = { + x: event.clientX - rect.left, + y: event.clientY - rect.top, + }; + const percentage = { + x: (100 / rect.width) * position.x, + y: (100 / rect.height) * position.y, + }; + const delta = { + x: percentage.x - 50, + y: percentage.y - 50, + }; + + const { background, rotate, glare } = state.current; + background.x = 50 + percentage.x / 4 - 12.5; + background.y = 50 + percentage.y / 3 - 16.67; + rotate.x = -(delta.x / 3.5); + rotate.y = delta.y / 2; + rotate.x *= rotateFactor; + rotate.y *= rotateFactor; + glare.x = percentage.x; + glare.y = percentage.y; + + updateStyles(); + }} + onPointerEnter={() => { + isPointerInside.current = true; + if (refElement.current) { + setTimeout(() => { + if (isPointerInside.current) { + refElement.current?.style.setProperty("--duration", "0s"); + } + }, 300); + } + }} + onPointerLeave={() => { + isPointerInside.current = false; + if (refElement.current) { + refElement.current.style.removeProperty("--duration"); + refElement.current?.style.setProperty("--r-x", `0deg`); + refElement.current?.style.setProperty("--r-y", `0deg`); + } + }} + > +
+
+
+ {children} +
+
+
+
+ +
+
+ ); +}; \ No newline at end of file diff --git a/apps/web/app/(landing)/Cta.tsx b/apps/web/app/(landing)/Cta.tsx index f0f471c2..e163a8f0 100644 --- a/apps/web/app/(landing)/Cta.tsx +++ b/apps/web/app/(landing)/Cta.tsx @@ -1,14 +1,17 @@ -import Image from "next/image"; import React from "react"; import EmailInput from "./EmailInput"; +import { ChevronRight } from "lucide-react"; function Cta() { return (
-
+
+
+ +
{/* a blue gradient line that's slightly tilted with blur (a lotof blur)*/}
- Landing page background -

+

+

+ Launching July 1st, 2024 + +

+

+

Your bookmarks are collecting dust.

-
- Launching July 1st, 2024 -
-

- Time to change that.
Sign up for the waitlist and be the first to - try Supermemory +

+ Sign up for the waitlist and be the first to try Supermemory

- +
+ +
); } diff --git a/apps/web/app/(landing)/EmailInput.tsx b/apps/web/app/(landing)/EmailInput.tsx index ac1f7fca..936b8835 100644 --- a/apps/web/app/(landing)/EmailInput.tsx +++ b/apps/web/app/(landing)/EmailInput.tsx @@ -3,16 +3,18 @@ import { FormEvent, useState } from "react"; import formSubmitAction from "./formSubmitAction"; import { useToast } from "@repo/ui/shadcn/use-toast"; +import { Loader } from "lucide-react"; function EmailInput() { const [email, setEmail] = useState(""); const { toast } = useToast(); + const [loading, setLoading] = useState(false); return (
) => { e.preventDefault(); - + setLoading(true); const value = await formSubmitAction(email, "token" as string); if (value.success) { @@ -30,16 +32,17 @@ function EmailInput() { description: `${value.value}`, }); } + setLoading(false); }} className="flex w-full items-center justify-center gap-2" >
- - - + {loading ? ( + + ) : ( + + + + )} )} diff --git a/apps/web/app/(landing)/FeatureCardContent.tsx b/apps/web/app/(landing)/FeatureCardContent.tsx new file mode 100644 index 00000000..1b38cd65 --- /dev/null +++ b/apps/web/app/(landing)/FeatureCardContent.tsx @@ -0,0 +1,102 @@ +import { Twitter } from "@repo/ui/components/icons"; +import { GlareCard } from "./CardPatterns/Glare"; +import React from "react"; +import { MessageCircle, Search } from "lucide-react"; + +export default function FUIFeatureSectionWithCards() { + const features = [ + { + icon: , + title: "Twitter Bookmarks", + desc: "Use all the knowledge you've saved on Twitter to train your own supermemory..", + }, + { + icon: , + title: "Powerful search", + desc: "Look up anything you've saved in your supermemory, and get the information you need in seconds.", + }, + { + icon: , + title: "Chat with collections", + desc: "Use collections to talk to specific knowledgebases like 'My twitter bookmarks', or 'Learning web development'", + }, + { + icon: ( + + + + ), + title: "Knowledge canvas", + desc: " Arrange your saved information in a way that makes sense to youin a 2d canvas.", + }, + { + icon: ( + + + + ), + title: "Just... bookmarks", + desc: "AI is cool, but sometimes you just need a place to save your stuff. Supermemory is that place.", + }, + { + icon: ( + + + + ), + title: "Writing assistant", + desc: " Use our markdown editor to write content based on your saved data, with the help of AI.", + }, + ]; + + return ( +
+
+
+
+

+

+ +
+
+
+
+
    + {features.map((item, idx) => ( + +
  • +
    + {item.icon} +
    +

    + {item.title} +

    +

    {item.desc}

    +
  • +
    + ))} +
+
+
+
+ ); +} diff --git a/apps/web/app/(landing)/Features.tsx b/apps/web/app/(landing)/Features.tsx index c5737b57..55634d0c 100644 --- a/apps/web/app/(landing)/Features.tsx +++ b/apps/web/app/(landing)/Features.tsx @@ -1,11 +1,8 @@ "use client"; - import { useState, useRef, useEffect } from "react"; -import Image from "next/image"; -import { X } from "@repo/ui/components/icons"; - -import { features } from "./FeatureContent"; -import { CardClick } from "@repo/ui/components/cardClick"; +import FUIFeatureSectionWithCards from "./FeatureCardContent"; +import { ArrowUpRight } from "lucide-react"; +import Link from "next/link"; export default function Features() { const [tab, setTab] = useState(0); @@ -26,223 +23,43 @@ export default function Features() { }, []); return ( -
-
- {/* Carousel */} -
-
- {/* Content */} -
-
-
- Use cases -
-

- Save time and keep things organised -

-

- With Supermemory, it's really easy to save information from - all over the internet, while training your own AI to help you - do more with it. -

-
- {/* Tabs buttons */} -
- -
-
+
+ +
+
+
+

+ A "Second brain" made for you... or your team +

- {/* Tabs items */} -
-
- {/* Item 1 */} -
-
- Carousel 01 -
-
- {/* Item 2 */} -
-
- Carousel 02 -
-
- {/* Item 3 */} -
-
- Carousel 03 -
-
+

+ Supermemory offers all the vital building blocks you need to + transform your gold mine of content into a powerful knowledgebase + for yourself, your team or even a group of friends! +

+ + Get Started +
+ +
-
+
-
- - {/* Features blocks */} -
-
- {/* Block #1 */} -
-
- -

- Import all your Twitter bookmarks -

-
-

- Use all the knowledge you've saved on Twitter to train your own - supermemory. -

-
- {/* Block #2 */} -
-
- - - -

- Chat with collections -

-
-

- Use collections to talk to specific knowledgebases like 'My - twitter bookmarks', or 'Learning web development' -

-
- {/* Block #3 */} -
-
- - - -

- Powerful search -

-
-

- Look up anything you've saved in your supermemory, and get the - information you need in seconds. -

-
- {/* Block #4 */} -
-
- - - -

- Knowledge canvas -

-
-

- Arrange your saved information in a way that makes sense to you - in a 2d canvas. -

-
- {/* Block #5 */} -
-
- - - -

- Just... bookmarks -

-
-

- AI is cool, but sometimes you just need a place to save your - stuff. Supermemory is that place. -

-
- {/* Block #6 */} -
-
- - - -

- Writing assistant -

-
-

- Use our markdown editor to write content based on your saved - data, with the help of AI. -

-
+ +
+
diff --git a/apps/web/app/(landing)/Features/chatbubble.tsx b/apps/web/app/(landing)/Features/chatbubble.tsx new file mode 100644 index 00000000..96a3f9b7 --- /dev/null +++ b/apps/web/app/(landing)/Features/chatbubble.tsx @@ -0,0 +1,17 @@ +const ChatBubbleWing = ({ className, pathClassName }: {className?:string , pathClassName?:string}) => { + return ( + + + + ); + }; + + export default ChatBubbleWing; \ No newline at end of file diff --git a/apps/web/app/(landing)/Features/features.tsx b/apps/web/app/(landing)/Features/features.tsx new file mode 100644 index 00000000..c2e4634a --- /dev/null +++ b/apps/web/app/(landing)/Features/features.tsx @@ -0,0 +1,19 @@ +import { cn } from "@repo/ui/lib/utils"; + +export const Gradient = ({ opacity = 50 }: { opacity?: number }) => { + return ( +
+
+ +
+ ); +}; + diff --git a/apps/web/app/(landing)/Features/generating.tsx b/apps/web/app/(landing)/Features/generating.tsx new file mode 100644 index 00000000..6cd9ad73 --- /dev/null +++ b/apps/web/app/(landing)/Features/generating.tsx @@ -0,0 +1,16 @@ +import { Loader, Loader2 } from "lucide-react"; + +const Generating = ({ className }: { className?: string }) => { + return ( +
+ + Searching your second brain... +
+ ); +}; + +export default Generating; diff --git a/apps/web/app/(landing)/Features/index.tsx b/apps/web/app/(landing)/Features/index.tsx new file mode 100644 index 00000000..18775d89 --- /dev/null +++ b/apps/web/app/(landing)/Features/index.tsx @@ -0,0 +1,229 @@ +import { CheckIcon, ChevronRight, GithubIcon } from "lucide-react"; +import { Gradient } from "./features"; +import Generating from "./generating"; +import Image from "next/image"; +import { AnimatedBeamShow } from "../CardPatterns/AnimatedBeamWithOutput"; + +const Services = () => { + return ( +
+
+
+

+ Sounds super cool? There's more. +

+

+
+ +
+
+ + +
+ Smartest AI +
+ +
+

+ We paid attention to details. +

+

+ a small team of 4 student developers who have one mission.{" "} +
+ Make the best second brain for everyone. +

+
    + {supermemoryPoints.map((item, index) => ( +
  • + + {/* */} +

    {item}

    +
  • + ))} +
+
+ + +
+ +
+
+
+
+ + robot +
+ +
+

+ Supermemory works everywhere you are. +

+

+ We already have integrations for Telegram and Twitter (X), + with whatsapp and SMS coming soon. So you can add and query + data in a private manner, from anywhere. +

+ + Get started + + +
+
+ +
+
+

+ Privacy First +

+

+ We use state-of-the art technology and providers to make sure + that your data is completely safe and secure, and only store + what's absolutely needed. +

+
+ +
+ vault image +
+ + +
+
+ +
+ + +
+ Github +
+ +
+

+ Proudly
Open Source +

+

+ You dont even need to trust us. Just deploy it yourself and + enjoy the benefits. +

+ + {" "} + Star us on Github + + +
+
+ +
+
+
+ +
+ +
+

+ Bringing content in is easy. +

+

+ You can use our chrome extension, iOS shortcut, or our API to + send content to supermemory. +

+
+
+ +
+
+ +
+

+ Self hostable +

+

+ All the code is open source and self hostable for + non-commercial use. +

+
+ +
+ Scary robot +
+ + +
+
+ + +
+
+
+ ); +}; + +export default Services; +const supermemoryPoints = [ + "Privacy focused", + "Works everywhere you are", + "Self hostable", + "Super affordable, with a generous free tier", +]; diff --git a/apps/web/app/(landing)/GridPatterns/PlusGrid.tsx b/apps/web/app/(landing)/GridPatterns/PlusGrid.tsx new file mode 100644 index 00000000..937ac904 --- /dev/null +++ b/apps/web/app/(landing)/GridPatterns/PlusGrid.tsx @@ -0,0 +1,45 @@ +interface PlusPatternBackgroundProps { + plusSize?: number; + plusColor?: string; + backgroundColor?: string; + className?: string; + style?: React.CSSProperties; + fade?: boolean; + [key: string]: any; +} + +export const BackgroundPlus: React.FC = ({ + plusColor = "#6b6b6b", + backgroundColor = "transparent", + plusSize = 60, + className, + fade = true, + style, + ...props +}) => { + const encodedPlusColor = encodeURIComponent(plusColor); + + const maskStyle: React.CSSProperties = fade + ? { + maskImage: "radial-gradient(circle, white 10%, transparent 90%)", + WebkitMaskImage: "radial-gradient(circle, white 10%, transparent 90%)", + } + : {}; + + const backgroundStyle: React.CSSProperties = { + backgroundColor, + backgroundImage: `url("data:image/svg+xml,%3Csvg width='${plusSize}' height='${plusSize}' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='${encodedPlusColor}' fill-opacity='0.2'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")`, + ...maskStyle, + ...style, + }; + + return ( +
+ ); +}; + +export default BackgroundPlus; diff --git a/apps/web/app/(landing)/Headers/Navbar.tsx b/apps/web/app/(landing)/Headers/Navbar.tsx new file mode 100644 index 00000000..0ba2c35c --- /dev/null +++ b/apps/web/app/(landing)/Headers/Navbar.tsx @@ -0,0 +1,120 @@ +import React, { useRef, useState } from "react"; +import { motion } from "framer-motion"; +import Logo from "../../../public/logo.svg"; +import Image from "next/image"; +import Link from "next/link"; +import { ArrowUpRight } from "lucide-react"; +export const SlideNavTabs = () => { + return ( +
+ +
+ ); +}; + +const SlideTabs = () => { + const [position, setPosition] = useState({ + left: 0, + width: 0, + opacity: 0, + }); + + return ( +
    { + setPosition((pv) => ({ + ...pv, + opacity: 0, + })); + }} + className="flex relative items-center py-3 px-5 mx-auto text-sm text-gray-200 bg-gradient-to-tr to-transparent rounded-full border-2 w-fit border-white/5 from-zinc-300/5 via-gray-400/5 shadow-[0px_2px_3px_-1px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(25,28,33,0.02),0px_0px_0px_1px_rgba(25,28,33,0.08)] backdrop-blur-lg backdrop-filter" + > + + Supermemory logo + + + + + Home + + + + + Use cases + + + + + Features + + + + + Team + + + + + Join waitlist +
    + + +
    + + + +
+ ); +}; +const Tab = ({ + children, + setPosition, +}: { + children: React.ReactNode; + setPosition: ({ + left, + width, + opacity, + }: { + left: number; + width: number; + opacity: number; + }) => void; +}) => { + const ref = useRef(null); + + return ( +
  • { + if (!ref?.current) return; + const { width } = ref.current.getBoundingClientRect(); + setPosition({ + left: ref.current.offsetLeft, + width, + opacity: 1, + }); + }} + className="block relative z-10 py-2.5 px-3 text-xs text-white uppercase cursor-pointer md:py-2 md:px-5 md:text-base mix-blend-difference" + > + {children} +
  • + ); +}; +// @ts-ignore +const Cursor = ({ position }) => { + return ( + + ); +}; diff --git a/apps/web/app/(landing)/Hero.tsx b/apps/web/app/(landing)/Hero.tsx index 6807b92d..1ae78f0c 100644 --- a/apps/web/app/(landing)/Hero.tsx +++ b/apps/web/app/(landing)/Hero.tsx @@ -5,6 +5,7 @@ import { Twitter } from "@repo/ui/components/icons"; import EmailInput from "./EmailInput"; import LinkArrow from "./linkArrow"; import { TwitterBorder } from "./twitterLink"; +import AnimatedLogoCloud from "./ImageSliders"; const slap = { initial: { @@ -22,41 +23,54 @@ const slap = { function Hero() { return ( <> -
    +
    - Build your own second brain with Supermemory + Unlock your{" "} + + digital brain + {" "} + with supermemory - Bring saved information from all over the internet into one place - where you can connect it, and ask AI about it + Supermemory is your ultimate hub for organizing, searching, and + utilizing saved information with powerful tools like a search engine, + writing assistant, and canvas.
    - + + +
    + +
    +
    ); } diff --git a/apps/web/app/(landing)/ImageSliders.tsx b/apps/web/app/(landing)/ImageSliders.tsx new file mode 100644 index 00000000..143e7c78 --- /dev/null +++ b/apps/web/app/(landing)/ImageSliders.tsx @@ -0,0 +1,210 @@ +import { + // Github, + Medium, + Notion, + Reddit, + Twitter, +} from "@repo/ui/components/icons"; +import Image from "next/image"; +const Github = ({ className }: { className?: string }) => { + return ( + + + + + + + + + + + + + + + + + + + + ); +}; +const NotionText = ({ className }: { className?: string }) => { + return ( + + + + + + + + ); +}; +const MediumText = ({ className }: { className?: string }) => { + return ( + + + + ); +}; +const logos = [ + { + name: "Github", + url: , + }, + { + name: "Medium", + url: , + }, + { + name: "Notion", + url: , + }, + { + name: "Reddit", + url: , + }, + { + name: "Twitter", + url: , + }, +]; + +const AnimatedLogoCloud = () => { + return ( +
    +

    + Works perfectly with the apps you love. +

    + {/*
    */} + +
    +
    +
    +
    +
    + {Array(5) + .fill(null) + .map((index) => ( +
    + {logos.map((logo, key) => ( + <>{logo.url} + ))} +
    + ))} +
    +
    +
    + ); +}; + +export default AnimatedLogoCloud; diff --git a/apps/web/app/(landing)/Navbar.tsx b/apps/web/app/(landing)/Navbar.tsx index 6a4bfa2f..18dcc2d1 100644 --- a/apps/web/app/(landing)/Navbar.tsx +++ b/apps/web/app/(landing)/Navbar.tsx @@ -12,31 +12,12 @@ import { useScroll, useMotionValueEvent, } from "framer-motion"; +import { SlideNavTabs } from "./Headers/Navbar"; function NavbarContent() { return ( -
    - - Supermemory logo - -
    - - Use cases - - - Features - - - Try supermemory - -
    - - Login - - +
    +
    ); } diff --git a/apps/web/app/(landing)/RotatingIcons.tsx b/apps/web/app/(landing)/RotatingIcons.tsx index 87546dde..39d19d62 100644 --- a/apps/web/app/(landing)/RotatingIcons.tsx +++ b/apps/web/app/(landing)/RotatingIcons.tsx @@ -30,14 +30,19 @@ const icons = [ const RotatingIcons: React.FC = () => { return ( -
    -

    - Collect data from
    {" "} - - any website{" "} - {" "} - on the internet -

    +
    + + Collect any data{" "} + + any website on the internet + + +
    diff --git a/apps/web/app/(landing)/Showcase.tsx b/apps/web/app/(landing)/Showcase.tsx new file mode 100644 index 00000000..8e00baf6 --- /dev/null +++ b/apps/web/app/(landing)/Showcase.tsx @@ -0,0 +1,280 @@ +"use client"; + +import { useId } from "react"; +import Image, { type ImageProps } from "next/image"; +import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@headlessui/react"; +import clsx from "clsx"; +import TestImg from "../../public/images/carousel-illustration-01.png"; +import Search from "../../public/images/search.svg"; +import Memroy from "../../public/images/memory.svg"; +interface Feature { + name: React.ReactNode; + summary: string; + description: string; + image: ImageProps["src"]; + icon: React.ComponentType; +} + +// TODO: This features section will be more for "use-cases" +const features: Array = [ + { + name: "Ideation", + summary: + "Never lose a great idea again - instead of saving it in your head, save it in supermemory.", + description: + "The internet is full of great ideas, but there's a problem. They are ephemeral. They come and go. ", + image: "search.svg", + icon: function ReportingIcon() { + let id = useId(); + return ( + <> + + + + + + + + + ); + }, + }, + { + name: "Bookmarks", + summary: "Simply great bookmarking tool.", + description: + "Good bookmarking tools have ways to import and organise your bookmarks. Great bookmarking tool resurfaces them when you need them.", + image: "memory.svg", + icon: function InventoryIcon() { + return ( + <> + + + + + ); + }, + }, + { + name: "Contacts", + summary: "Life is all about the people you know.", + description: + "Tell supermemory about people you know, and when you forget, you know where to look.", + image: "search.svg", + icon: function ContactsIcon() { + return ( + <> + + + + ); + }, + }, +]; + +function Feature({ + feature, + isActive, + className, + ...props +}: React.ComponentPropsWithoutRef<"div"> & { + feature: Feature; + isActive: boolean; +}) { + return ( +
    +
    + +
    +

    + {feature.name} +

    +

    + {feature.summary} +

    +

    {feature.description}

    +
    + ); +} + +function FeaturesMobile() { + return ( +
    + {features.map((feature) => ( +
    + +
    +
    +
    + +
    +
    +
    + ))} +
    + ); +} + +function FeaturesDesktop() { + return ( + + {({ selectedIndex }) => ( + <> + + {features.map((feature, featureIndex) => ( + + + {feature.name} + + ), + }} + isActive={featureIndex === selectedIndex} + className="relative" + /> + ))} + + +
    + {features.map((feature, featureIndex) => ( + +
    + +
    +
    + ))} +
    +
    + + + )} + + ); +} + +export function Showcases() { + return ( +
    + +
    +
    + {/* back bg */} +
    +

    + + Supermemory
    +
    {" "} + remembers everything. +

    +

    + ... so you don't have to. Whether you're a student, a professional, + or just a person on the the internet. we got you covered. +

    +
    +
    +
    +
    + + + +
    + ); +} + +export function Container({ + className, + ...props +}: React.ComponentPropsWithoutRef<"div">) { + return ( +
    + ); +} diff --git a/apps/web/app/(landing)/footer.tsx b/apps/web/app/(landing)/footer.tsx index 4ebfca0b..48abc048 100644 --- a/apps/web/app/(landing)/footer.tsx +++ b/apps/web/app/(landing)/footer.tsx @@ -3,7 +3,7 @@ import LinkArrow from "./linkArrow"; function Footer() { return ( -