mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-05 08:06:19 +00:00
added all the fixes around onboarding, dashboard
This commit is contained in:
parent
af6ab08c53
commit
ed6b6fb2fc
20 changed files with 505 additions and 211 deletions
|
|
@ -33,7 +33,7 @@ function UserSupermemory({ name }: { name: string }) {
|
|||
>
|
||||
<Logo className="h-14 text-white" />
|
||||
<div className="flex flex-col items-start justify-center ml-4">
|
||||
<p className="text-white text-xl leading-none">{name}'s</p>
|
||||
<p className="text-white text-xl font-medium leading-none">{name}'s</p>
|
||||
<p className="text-white font-bold text-4xl leading-none -mt-2">
|
||||
supermemory
|
||||
</p>
|
||||
|
|
@ -211,7 +211,7 @@ export default function OnboardingPage() {
|
|||
: currentStep === "memories"
|
||||
? 0.4
|
||||
: 1,
|
||||
padding: minimizeNovaOrb ? 0 : 32,
|
||||
padding: minimizeNovaOrb ? 0 : 48,
|
||||
}}
|
||||
transition={{
|
||||
duration: 0.8,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import { useState } from "react"
|
||||
import { Button } from "@ui/components/button"
|
||||
import { MCPDetailView } from "@/components/mcp-detail-view"
|
||||
import { ChromeExtensionDetailView } from "@/components/chrome-extension-detail-view"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
|
|
@ -69,31 +70,41 @@ export function IntegrationsStep() {
|
|||
if (selectedCard === "Connect to AI") {
|
||||
return <MCPDetailView onBack={() => setSelectedCard(null)} />
|
||||
}
|
||||
if (selectedCard === "Capture") {
|
||||
return <ChromeExtensionDetailView onBack={() => setSelectedCard(null)} />
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full p-8">
|
||||
<div className="text-center mb-6 flex flex-col items-center justify-center space-y-2">
|
||||
<h1 className="text-white text-3xl font-medium">
|
||||
<h1 className="text-white text-[32px] font-medium">
|
||||
Build your personal memory
|
||||
</h1>
|
||||
<p className={cn("text-white text-sm opacity-60 max-w-xs", dmSansClassName())}>
|
||||
Your supermemory comes alive when you capture and connect what's
|
||||
important
|
||||
<p
|
||||
className={cn(
|
||||
"text-white text-sm opacity-60 max-w-xs",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
Your supermemory comes alive when you <br /> capture and connect
|
||||
what's important
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 max-w-lg w-full mb-12">
|
||||
{integrationCards.map((card) => {
|
||||
const isClickable = card.title === "Connect to AI"
|
||||
const isClickable =
|
||||
card.title === "Connect to AI" || card.title === "Capture"
|
||||
|
||||
if (isClickable) {
|
||||
return (
|
||||
<button
|
||||
key={card.title}
|
||||
type="button"
|
||||
className={cn("bg-[#080B0F] relative rounded-lg p-4 hover:border-[#3374FF] hover:border-[0.1px] transition-colors duration-300 border-[0.1px] border-[#0D121A] cursor-pointer text-left w-full hover:bg-[url('/onboarding/bg-gradient-1.png')] hover:bg-[length:175%_auto] hover:bg-[center_top_2rem] hover:bg-no-repeat",
|
||||
"hover:border-b-0 border-b-0"
|
||||
className={cn(
|
||||
"bg-[#080B0F] relative rounded-lg p-4 hover:border-[#3374FF] hover:border-[0.1px] transition-colors duration-300 border-[0.1px] border-[#0D121A] cursor-pointer text-left w-full hover:bg-[url('/onboarding/bg-gradient-1.png')] hover:bg-[length:175%_auto] hover:bg-[center_top_2rem] hover:bg-no-repeat",
|
||||
"hover:border-b-0 border-b-0",
|
||||
)}
|
||||
onClick={() => setSelectedCard("Connect to AI")}
|
||||
onClick={() => setSelectedCard(card.title)}
|
||||
>
|
||||
<div className="flex-1 mt-10">
|
||||
<h3 className="text-white text-sm font-medium">
|
||||
|
|
@ -111,7 +122,7 @@ export function IntegrationsStep() {
|
|||
return (
|
||||
<div
|
||||
key={card.title}
|
||||
className="bg-[#080B0F] relative rounded-lg p-4 hover:border-[#3374FF] hover:border-[0.1px] transition-colors duration-300 border-[0.1px] border-[#0D121A] hover:bg-[url('/onboarding/bg-gradient-1.png')] hover:bg-[length:175%_auto] hover:bg-[center_top_2rem] hover:bg-no-repeat"
|
||||
className="bg-[#080B0F] relative rounded-lg p-3 hover:border-[#3374FF] hover:border-[0.1px] transition-colors duration-300 border-[0.1px] border-[#0D121A] hover:bg-[url('/onboarding/bg-gradient-1.png')] hover:bg-[length:175%_auto] hover:bg-[center_top_2rem] hover:bg-no-repeat"
|
||||
>
|
||||
<div className="flex-1 mt-10">
|
||||
<h3 className="text-white text-sm font-medium">{card.title}</h3>
|
||||
|
|
@ -133,7 +144,11 @@ export function IntegrationsStep() {
|
|||
>
|
||||
← Back
|
||||
</Button>
|
||||
<Button variant="link" className="text-white hover:text-gray-300" onClick={handleContinue}>
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-white hover:text-gray-300"
|
||||
onClick={handleContinue}
|
||||
>
|
||||
Continue →
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export function RelatableQuestion() {
|
|||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<motion.h1
|
||||
className="text-white text-3xl font-medium mb-6 text-center"
|
||||
className="text-white text-[32px] font-medium mb-6 text-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export function FeaturesStep() {
|
|||
className="text-center max-w-[22rem] space-y-6"
|
||||
layout
|
||||
>
|
||||
<h2 className="text-white text-3xl font-medium">
|
||||
<h2 className="text-white text-[32px] font-medium">
|
||||
What I can do for you
|
||||
</h2>
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export function GreetingStep({ name }: GreetingStepProps) {
|
|||
transition={{ duration: 1, ease: "easeOut" }}
|
||||
layout
|
||||
>
|
||||
<h2 className="text-white text-2xl font-medium mb-2">
|
||||
<h2 className="text-white text-[32px] font-medium mb-2">
|
||||
Hi {name}, I'm Nova
|
||||
</h2>
|
||||
</motion.div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ export function InputStep({
|
|||
}: InputStepProps) {
|
||||
return (
|
||||
<motion.div
|
||||
className="text-center min-w-[250px]"
|
||||
className="text-center min-w-[250px] flex flex-col"
|
||||
style={{ gap: "24px" }}
|
||||
initial={{
|
||||
opacity: 0,
|
||||
y: 10,
|
||||
|
|
@ -42,7 +43,7 @@ export function InputStep({
|
|||
}}
|
||||
layout
|
||||
>
|
||||
<h2 className="text-white text-3xl font-medium mb-2">
|
||||
<h2 className="text-white text-[32px] font-medium">
|
||||
What should I call you?
|
||||
</h2>
|
||||
<div className="flex items-center w-full relative">
|
||||
|
|
@ -57,7 +58,7 @@ export function InputStep({
|
|||
handleSubmit()
|
||||
}
|
||||
},
|
||||
className: "!text-white placeholder:!text-[#525966] !h-10",
|
||||
className: "!text-white placeholder:!text-[#525966] !h-[40px] pl-4",
|
||||
}}
|
||||
onChange={(e) => setName((e.target as HTMLInputElement).value)}
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -32,30 +32,23 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
setOtherLinks(updated)
|
||||
}
|
||||
|
||||
const removeOtherLink = (index: number) => {
|
||||
if (otherLinks.length > 1) {
|
||||
const updated = otherLinks.filter((_, i) => i !== index)
|
||||
setOtherLinks(updated)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, ease: "easeOut", delay: 0.3 }}
|
||||
className="text-center max-w-[28rem] w-full"
|
||||
className="text-center w-full"
|
||||
layout
|
||||
>
|
||||
<h2 className="text-white text-3xl font-medium mb-4 mt-[-36px]">
|
||||
<h2 className="text-white text-[32px] font-medium mb-4 mt-[-36px]">
|
||||
Let's add your memories
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="text-left" id="x-twitter-field">
|
||||
<div className="space-y-4 max-w-[329px] mx-auto">
|
||||
<div className="text-left gap-[6px] flex flex-col" id="x-twitter-field">
|
||||
<label
|
||||
htmlFor="twitter-handle"
|
||||
className="text-white text-sm font-medium mb-2 block pl-2"
|
||||
className="text-white text-sm font-medium block pl-2"
|
||||
>
|
||||
X/Twitter
|
||||
</label>
|
||||
|
|
@ -65,14 +58,14 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
placeholder="x.com/yourhandle"
|
||||
value={twitterHandle}
|
||||
onChange={(e) => setTwitterHandle(e.target.value)}
|
||||
className="w-full px-4 py-2 bg-[#070E1B] border border-[#525966]/20 rounded-xl text-white placeholder-onboarding focus:outline-none focus:border-[#4A4A4A] transition-colors"
|
||||
className="w-full px-4 py-2 bg-[#070E1B] border border-[#525966]/20 rounded-xl text-white placeholder-onboarding focus:outline-none focus:border-[#4A4A4A] transition-colors h-[40px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="text-left" id="linkedin-field">
|
||||
<div className="text-left gap-[6px] flex flex-col" id="linkedin-field">
|
||||
<label
|
||||
htmlFor="linkedin-profile"
|
||||
className="text-white text-sm font-medium mb-2 block pl-2"
|
||||
className="text-white text-sm font-medium block pl-2"
|
||||
>
|
||||
LinkedIn
|
||||
</label>
|
||||
|
|
@ -82,19 +75,22 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
placeholder="linkedin.com/in/yourname"
|
||||
value={linkedinProfile}
|
||||
onChange={(e) => setLinkedinProfile(e.target.value)}
|
||||
className="w-full px-4 py-2 bg-[#070E1B] border border-[#525966]/20 rounded-xl text-white placeholder-onboarding focus:outline-none focus:border-[#4A4A4A] transition-colors"
|
||||
className="w-full px-4 py-2 bg-[#070E1B] border border-[#525966]/20 rounded-xl text-white placeholder-onboarding focus:outline-none focus:border-[#4A4A4A] transition-colors h-[40px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="text-left" id="other-links-field">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div
|
||||
className="text-left gap-[6px] flex flex-col"
|
||||
id="other-links-field"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<label
|
||||
htmlFor="other-links"
|
||||
className="text-white text-sm font-medium pl-2"
|
||||
>
|
||||
Other links
|
||||
</label>
|
||||
<span className="text-[#8A8A8A] text-xs">Upto 3</span>
|
||||
<span className="text-[#525966] text-[10px]">Upto 3</span>
|
||||
</div>
|
||||
{otherLinks.map((link, index) => (
|
||||
<div
|
||||
|
|
@ -107,7 +103,7 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
placeholder="Add your website, GitHub, Notion..."
|
||||
value={link}
|
||||
onChange={(e) => updateOtherLink(index, e.target.value)}
|
||||
className="flex-1 px-4 py-2 bg-[#070E1B] border border-[#525966]/20 rounded-xl text-white placeholder-onboarding focus:outline-none focus:border-[#4A4A4A] transition-colors"
|
||||
className="flex-1 px-4 py-2 bg-[#070E1B] border border-[#525966]/20 rounded-xl text-white placeholder-onboarding focus:outline-none focus:border-[#4A4A4A] transition-colors h-[40px]"
|
||||
/>
|
||||
{index === otherLinks.length - 1 && otherLinks.length < 3 && (
|
||||
<button
|
||||
|
|
@ -122,10 +118,13 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
))}
|
||||
</div>
|
||||
|
||||
<div className="text-left" id="description-field">
|
||||
<div
|
||||
className="text-left gap-[6px] flex flex-col"
|
||||
id="description-field"
|
||||
>
|
||||
<label
|
||||
htmlFor="description"
|
||||
className="text-white text-sm font-medium mb-2 block pl-2"
|
||||
className="text-white text-sm font-medium block pl-2"
|
||||
>
|
||||
What do you do? What do you like?
|
||||
</label>
|
||||
|
|
@ -134,7 +133,7 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
placeholder="Tell me the basics in your words. A few lines about your work, interests, etc."
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
rows={4}
|
||||
rows={2}
|
||||
className="w-full px-4 py-2 bg-[#070E1B] border border-[#525966]/20 rounded-xl text-white placeholder-onboarding focus:outline-none focus:border-[#4A4A4A] transition-colors min-h-[4rem]"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -147,11 +146,14 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
}}
|
||||
transition={{ duration: 1, ease: "easeOut", delay: 1 }}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
className="mt-6"
|
||||
className="mt-[24px]"
|
||||
>
|
||||
<Button
|
||||
className="rounded-xl px-6 py-3 bg-[#070E1B] border border-[#525966]/20 hover:bg-[#2A2A2A] max-w-[12rem] text-white disabled:opacity-50 disabled:cursor-not-allowed h-[40px] cursor-pointer"
|
||||
className="rounded-xl px-6 py-3 border border-[#525966]/20 max-w-[12rem] text-white disabled:opacity-50 disabled:cursor-not-allowed h-[40px] cursor-pointer"
|
||||
disabled={isSubmitting}
|
||||
style={{
|
||||
background: "linear-gradient(180deg, #0D121A -26.14%, #000 100%)",
|
||||
}}
|
||||
onClick={() => {
|
||||
const formData = {
|
||||
twitter: twitterHandle,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export function WelcomeStep() {
|
|||
transition={{ duration: 1, ease: "easeOut" }}
|
||||
layout
|
||||
>
|
||||
<h2 className="text-white text-2xl font-medium mb-2">Welcome to</h2>
|
||||
<h2 className="text-white text-[32px] font-medium mb-2">Welcome to</h2>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
113
apps/web/components/chrome-extension-detail-view.tsx
Normal file
113
apps/web/components/chrome-extension-detail-view.tsx
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
"use client"
|
||||
|
||||
import { Button } from "@ui/components/button"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import Image from "next/image"
|
||||
|
||||
interface ChromeExtensionDetailViewProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
const steps = [
|
||||
{
|
||||
number: 1,
|
||||
title: "Install the Chrome Extension and login with your supermemory",
|
||||
image: "/onboarding/chrome-ext-1.png",
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
title: "Visit the bookmarks tab on X and one-click import your bookmarks",
|
||||
image: "/onboarding/chrome-ext-2.png",
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
title: "Talk to your bookmarks via Nova & see it in your memory graph",
|
||||
image: "/onboarding/chrome-ext-3.png",
|
||||
},
|
||||
]
|
||||
|
||||
export function ChromeExtensionDetailView({
|
||||
onBack,
|
||||
}: ChromeExtensionDetailViewProps) {
|
||||
const handleInstall = () => {
|
||||
window.open(
|
||||
"https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnangednailhoegogi",
|
||||
"_blank",
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full p-8">
|
||||
<div className="mb-6">
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-white hover:text-gray-300 p-0"
|
||||
onClick={onBack}
|
||||
>
|
||||
← Back
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start justify-start flex-1">
|
||||
<div>
|
||||
<h1 className="text-white text-[20px] font-medium mb-4 text-start">
|
||||
Import your X bookmarks via the Chrome Extension
|
||||
</h1>
|
||||
|
||||
<p
|
||||
className={cn(
|
||||
"text-[#8B8B8B] text-sm mb-12 text-start max-w-2xl",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
Bring your X bookmarks into Supermemory in just a few clicks.
|
||||
They'll be automatically embedded so you can easily find what you
|
||||
need, right when you need it.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-3 gap-6 mb-12 max-w-5xl w-full">
|
||||
{steps.map((step) => (
|
||||
<div
|
||||
key={step.number}
|
||||
className="flex flex-col items-center text-center bg-[#080B0F] p-3 rounded-[10px]"
|
||||
>
|
||||
<div className="rounded-2xl p-6 mb-4 w-full aspect-[4/4] flex items-center justify-center relative overflow-hidden">
|
||||
<Image
|
||||
src={step.image}
|
||||
alt={`Step ${step.number}`}
|
||||
fill
|
||||
className="object-cover"
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-start justify-start">
|
||||
<div className="mb-2">
|
||||
<span className="text-white font-medium">
|
||||
Step {step.number}
|
||||
</span>
|
||||
</div>
|
||||
<p
|
||||
className={cn(
|
||||
"text-[#8B8B8B] text-sm text-start",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
{step.title}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="rounded-xl px-6 py-3 text-white h-[44px] cursor-pointer mx-auto bg-black"
|
||||
onClick={handleInstall}
|
||||
>
|
||||
Install Chrome Extension →
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -9,12 +9,13 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@ui/components/select"
|
||||
import { CircleCheckIcon, CopyIcon } from "lucide-react"
|
||||
import { CircleCheckIcon, CopyIcon, Check } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import { toast } from "sonner"
|
||||
import { analytics } from "@/lib/analytics"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmMonoClassName, dmSansClassName } from "@/utils/fonts"
|
||||
import { SyncLogoIcon } from "@ui/assets/icons"
|
||||
|
||||
const clients = {
|
||||
cursor: "Cursor",
|
||||
|
|
@ -61,8 +62,9 @@ export function MCPDetailView({ onBack }: MCPDetailViewProps) {
|
|||
const command = generateInstallCommand()
|
||||
navigator.clipboard.writeText(command)
|
||||
analytics.mcpInstallCmdCopied()
|
||||
toast.success("Copied to clipboard!")
|
||||
setIsCopied(true)
|
||||
setActiveStep(3)
|
||||
setTimeout(() => setIsCopied(false), 2000)
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -84,73 +86,139 @@ export function MCPDetailView({ onBack }: MCPDetailViewProps) {
|
|||
|
||||
<div className="mb-8 space-x-4 flex max-w-2xl">
|
||||
<div className={cn("flex items-start space-x-3", dmSansClassName())}>
|
||||
<CircleCheckIcon className="size-10 text-green-500" />
|
||||
<p className="text-white/40 text-sm">
|
||||
<CircleCheckIcon className="size-4 text-green-500 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-[#8B8B8B] text-sm">
|
||||
MCP connects your AI apps to create and use memories directly
|
||||
</p>
|
||||
</div>
|
||||
<div className={cn("flex items-start space-x-3", dmSansClassName())}>
|
||||
<CircleCheckIcon className="size-10 text-green-500" />
|
||||
<p className="text-white/40 text-sm">
|
||||
<CircleCheckIcon className="size-4 text-green-500 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-[#8B8B8B] text-sm">
|
||||
Auto-fetch the right context from anything you've saved
|
||||
</p>
|
||||
</div>
|
||||
<div className={cn("flex items-start space-x-3", dmSansClassName())}>
|
||||
<CircleCheckIcon className="size-10 text-green-500" />
|
||||
<p className="text-white/40 text-sm">
|
||||
<CircleCheckIcon className="size-4 text-green-500 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-[#8B8B8B] text-sm">
|
||||
One-time setup, seamless integration across your workflow
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full max-w-2xl relative">
|
||||
<div className="absolute left-4 top-0 w-[1px] h-full bg-[#1E293B] z-10" />
|
||||
<div
|
||||
className="absolute left-4 top-0 w-[1px] bg-[#1E293B] z-10"
|
||||
style={{ height: activeStep === 3 ? "calc(100% - 4rem)" : "100%" }}
|
||||
/>
|
||||
<div className="flex items-start space-x-4 z-20">
|
||||
<div className={cn(
|
||||
"rounded-full w-8 h-8 flex items-center justify-center text-sm font-medium flex-shrink-0 z-20 bg-[#161F2B] text-white",
|
||||
)} >
|
||||
<span style={activeStep === 1 ? {
|
||||
background: "linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
} : undefined}>1</span>
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-full w-8 h-8 flex items-center justify-center text-sm font-medium flex-shrink-0 z-20 bg-[#161F2B] text-white",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
style={
|
||||
activeStep === 1
|
||||
? {
|
||||
background:
|
||||
"linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
1
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-1 mb-4">
|
||||
<div className="flex gap-4 mb-4">
|
||||
<h3 className="text-white text-lg font-medium" style={activeStep === 1 ? {
|
||||
background: "linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
} : undefined}>
|
||||
<h3
|
||||
className="text-white text-lg font-medium"
|
||||
style={
|
||||
activeStep === 1
|
||||
? {
|
||||
background:
|
||||
"linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
Select your AI client
|
||||
</h3>
|
||||
{selectedClient && <Select
|
||||
onValueChange={(value) => {
|
||||
setSelectedClient(value as keyof typeof clients)
|
||||
setActiveStep(2)
|
||||
}}
|
||||
value={selectedClient || undefined}
|
||||
>
|
||||
<SelectTrigger className="max-w-md rounded-full border-[#242A33] text-white hover:border-gray-600 !bg-transparent">
|
||||
<SelectValue placeholder="Select a client" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-black border-none">
|
||||
{Object.entries(clients).slice(0, 7).map(([key, clientName]) => (
|
||||
<SelectItem
|
||||
key={key}
|
||||
value={key}
|
||||
className="text-white hover:bg-[#080B0F]"
|
||||
>
|
||||
{clientName}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
}
|
||||
{selectedClient && (
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
setSelectedClient(value as keyof typeof clients)
|
||||
setActiveStep(2)
|
||||
}}
|
||||
value={selectedClient || undefined}
|
||||
>
|
||||
<SelectTrigger
|
||||
className="max-w-md rounded-full border-[#242A33] text-white hover:border-gray-600 !bg-transparent"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(0deg, #0A0E14 0%, #080B0F 100%)",
|
||||
}}
|
||||
>
|
||||
{selectedClient ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<Image
|
||||
alt={clients[selectedClient]}
|
||||
height={20}
|
||||
width={20}
|
||||
unoptimized
|
||||
src={
|
||||
selectedClient === "mcp-url"
|
||||
? "/mcp-icon.svg"
|
||||
: `/mcp-supported-tools/${selectedClient === "claude-code" ? "claude" : selectedClient}.png`
|
||||
}
|
||||
/>
|
||||
<span>{clients[selectedClient]}</span>
|
||||
</div>
|
||||
) : (
|
||||
<SelectValue placeholder="Select a client" />
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-black border-none">
|
||||
{Object.entries(clients)
|
||||
.slice(0, 7)
|
||||
.map(([key, clientName]) => (
|
||||
<SelectItem
|
||||
key={key}
|
||||
value={key}
|
||||
className="text-white hover:bg-[#080B0F]"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Image
|
||||
alt={clientName}
|
||||
height={20}
|
||||
width={20}
|
||||
unoptimized
|
||||
src={
|
||||
key === "mcp-url"
|
||||
? "/mcp-icon.svg"
|
||||
: `/mcp-supported-tools/${key === "claude-code" ? "claude" : key}.png`
|
||||
}
|
||||
/>
|
||||
<span>{clientName}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
<div className={cn("flex flex-wrap gap-2 mb-4", selectedClient ? "hidden" : "")}>
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-wrap gap-2 mb-4",
|
||||
selectedClient ? "hidden" : "",
|
||||
)}
|
||||
>
|
||||
{Object.entries(clients)
|
||||
.slice(0, 7)
|
||||
.map(([key, clientName]) => (
|
||||
|
|
@ -171,6 +239,7 @@ export function MCPDetailView({ onBack }: MCPDetailViewProps) {
|
|||
<div className="w-5 h-5 flex items-center justify-center">
|
||||
<Image
|
||||
alt={clientName}
|
||||
unoptimized
|
||||
className="rounded object-contain"
|
||||
height={20}
|
||||
onError={(e) => {
|
||||
|
|
@ -205,32 +274,53 @@ export function MCPDetailView({ onBack }: MCPDetailViewProps) {
|
|||
</button>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-gray-400 text-xs">
|
||||
*You can connect to all of these, setup is different for each
|
||||
one
|
||||
</p>
|
||||
{!selectedClient && (
|
||||
<p className="text-gray-400 text-xs">
|
||||
*You can connect to all of these, setup is different for each
|
||||
one
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className={cn(
|
||||
"rounded-full w-8 h-8 flex items-center justify-center text-sm font-medium flex-shrink-0 z-20 text-white bg-[#161F2B]",
|
||||
""
|
||||
)} >
|
||||
<span style={activeStep === 2 ? {
|
||||
background: "linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
} : undefined}>2</span>
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-full w-8 h-8 flex items-center justify-center text-sm font-medium flex-shrink-0 z-20 text-white bg-[#161F2B]",
|
||||
"",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
style={
|
||||
activeStep === 2
|
||||
? {
|
||||
background:
|
||||
"linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
2
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-1 mb-4">
|
||||
<h3 className="text-white text-lg font-medium mb-4" style={activeStep === 2 ? {
|
||||
background: "linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
} : undefined}>
|
||||
<h3
|
||||
className="text-white text-lg font-medium mb-4"
|
||||
style={
|
||||
activeStep === 2
|
||||
? {
|
||||
background:
|
||||
"linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
Copy the installation command
|
||||
</h3>
|
||||
{selectedClient && (
|
||||
|
|
@ -353,22 +443,39 @@ export function MCPDetailView({ onBack }: MCPDetailViewProps) {
|
|||
<div className="space-y-3">
|
||||
<div className="relative">
|
||||
<input
|
||||
className="font-mono text-xs w-full pr-10 p-2 bg-black border border-gray-600 rounded-lg text-green-400"
|
||||
className="font-mono text-xs w-full pr-10 p-4 px-2 bg-[#0D121A] rounded-xl text-white pl-3"
|
||||
style={{
|
||||
border: "1px solid rgba(61, 67, 77, 0.10)",
|
||||
}}
|
||||
readOnly
|
||||
value={generateInstallCommand()}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-1 right-1 cursor-pointer p-1"
|
||||
className={cn(
|
||||
"absolute top-1.5 right-1 cursor-pointer p-1 flex items-center rounded-[10px] px-3 py-2 gap-2",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(180deg, #267BF1 40.23%, #15468B 100%), linear-gradient(180deg, #0D121A -26.14%, #000 100%)",
|
||||
border: "1px solid #000",
|
||||
}}
|
||||
onClick={copyToClipboard}
|
||||
>
|
||||
<CopyIcon className="size-4 text-gray-400 hover:text-white" />
|
||||
{isCopied ? (
|
||||
<>
|
||||
<Check className="size-4 text-white" />
|
||||
<span className="text-white">Copied</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CopyIcon className="size-3 text-white" />
|
||||
<span className="text-white">Copy</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-gray-400 text-sm">
|
||||
Copy and run this command in your terminal to install
|
||||
the MCP server.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -377,26 +484,59 @@ export function MCPDetailView({ onBack }: MCPDetailViewProps) {
|
|||
</div>
|
||||
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className={cn(
|
||||
"rounded-full w-8 h-8 flex items-center justify-center text-sm font-medium flex-shrink-0 z-20",
|
||||
"bg-[#161F2B] text-white"
|
||||
)} >
|
||||
<span style={activeStep === 3 ? {
|
||||
background: "linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
} : undefined}>3</span>
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-full w-8 h-8 flex items-center justify-center text-sm font-medium flex-shrink-0 z-20",
|
||||
"bg-[#161F2B] text-white",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
style={
|
||||
activeStep === 3
|
||||
? {
|
||||
background:
|
||||
"linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
3
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-white text-lg font-medium" style={activeStep === 3 ? {
|
||||
background: "linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
} : undefined}>
|
||||
<div className="flex-1 space-y-4">
|
||||
<h3
|
||||
className="text-white text-lg font-medium"
|
||||
style={
|
||||
activeStep === 3
|
||||
? {
|
||||
background:
|
||||
"linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
Run command in your terminal
|
||||
</h3>
|
||||
{activeStep === 3 && (
|
||||
<p
|
||||
className={cn(
|
||||
"font-mono text-xs w-full pr-10 p-4 px-2 bg-[#0D121A] rounded-xl text-white pl-3 flex items-center gap-2",
|
||||
dmMonoClassName(),
|
||||
)}
|
||||
style={{
|
||||
border: "1px solid rgba(61, 67, 77, 0.10)",
|
||||
}}
|
||||
>
|
||||
<SyncLogoIcon className="size-4" />
|
||||
Waiting for installation
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -43,13 +43,12 @@ function getFileTypeInfo(document: DocumentWithMemories): {
|
|||
}
|
||||
}
|
||||
|
||||
// Fall back to document type
|
||||
switch (type) {
|
||||
case "pdf":
|
||||
return {
|
||||
icon: <PDF className="w-4 h-4 text-[#DC2626]" />,
|
||||
icon: <PDF className="w-4 h-4 text-[#FF7673]" />,
|
||||
extension: ".pdf",
|
||||
color: "#DC2626",
|
||||
color: "#FF7673",
|
||||
}
|
||||
case "image":
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,12 @@ export function NotePreview({ document }: { document: DocumentWithMemories }) {
|
|||
</div>
|
||||
<div>
|
||||
{document.title && (
|
||||
<p className={cn(dmSansClassName(), "text-[12px] font-semibold")}>
|
||||
<p
|
||||
className={cn(
|
||||
dmSansClassName(),
|
||||
"text-[12px] font-semibold line-clamp-2 leading-[125%]",
|
||||
)}
|
||||
>
|
||||
{document.title}
|
||||
</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -276,8 +276,8 @@ const DocumentCard = memo(
|
|||
WebkitTextFillColor: "transparent",
|
||||
}}
|
||||
>
|
||||
<SyncLogoIcon className="size-2" />
|
||||
{document.memoryEntries.length} memories
|
||||
<SyncLogoIcon className="w-[12.33px] h-[10px]" />
|
||||
{document.memoryEntries.length} {document.memoryEntries.length === 1 ? "memory" : "memories"}
|
||||
</p>
|
||||
<p
|
||||
className={cn(
|
||||
|
|
|
|||
BIN
apps/web/public/onboarding/chrome-ext-1.png
Normal file
BIN
apps/web/public/onboarding/chrome-ext-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
BIN
apps/web/public/onboarding/chrome-ext-2.png
Normal file
BIN
apps/web/public/onboarding/chrome-ext-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
BIN
apps/web/public/onboarding/chrome-ext-3.png
Normal file
BIN
apps/web/public/onboarding/chrome-ext-3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
|
|
@ -27,3 +27,13 @@ export function dmSansClassName(additionalClasses?: string) {
|
|||
additionalClasses,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export function dmMonoClassName(additionalClasses?: string) {
|
||||
return cn(
|
||||
dmMonoFont.className,
|
||||
"tracking-[-0.01em]",
|
||||
"leading-[135%]",
|
||||
additionalClasses,
|
||||
)
|
||||
}
|
||||
|
|
@ -15,13 +15,14 @@ export function ExternalAuthButton({
|
|||
return (
|
||||
<Button
|
||||
className={cn(
|
||||
"flex flex-grow cursor-pointer max-w-full items-center justify-center gap-[0.625rem] rounded-xl px-6 py-5 hover:opacity-75 h-11",
|
||||
"flex flex-grow cursor-pointer max-w-full items-center justify-center gap-[0.625rem] rounded-xl px-6 py-5 hover:opacity-75",
|
||||
className,
|
||||
)}
|
||||
style={{
|
||||
borderRadius: "12px",
|
||||
background: "linear-gradient(180deg, #00264F 0%, #001933 100%), linear-gradient(180deg, #0A0E14 0%, #05070A 100%)",
|
||||
boxShadow: "0 1px 2px 0 rgba(0, 43, 87, 0.10), 1px 1px 1px 1px #002B57 inset",
|
||||
height: "44px",
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -24,13 +24,14 @@ export function LabeledInput({
|
|||
{label && <Label1Regular className="text-foreground">{label}</Label1Regular>}
|
||||
<Input
|
||||
className={cn(
|
||||
"w-full leading-[1.375rem] tracking-[-0.4px] rounded-xl p-5 placeholder:text-muted-foreground/50 text-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"w-full leading-[1.375rem] tracking-[-0.4px] rounded-xl p-4 placeholder:text-muted-foreground/50 text-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||
inputProps?.className,
|
||||
)}
|
||||
style={{
|
||||
height: "44px",
|
||||
borderRadius: "12px",
|
||||
border: "1px solid rgba(82, 89, 102, 0.20)",
|
||||
background: "rgba(91, 126, 245, 0.04)",
|
||||
background: "#070E1B",
|
||||
boxShadow: "0 1px 2px 0 rgba(0, 43, 87, 0.10), 0 0 0 1px rgba(43, 49, 67, 0.08) inset, 0 1px 1px 0 rgba(0, 0, 0, 0.08) inset, 0 2px 4px 0 rgba(0, 0, 0, 0.02) inset",
|
||||
}}
|
||||
placeholder={inputPlaceholder}
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ export function LoginPage() {
|
|||
<AnimatedGradientBackground />
|
||||
<div className="relative z-10">
|
||||
<InitialHeader />
|
||||
<section className="flex flex-col items-center justify-center p-4 space-y-8 sm:p-6 md:p-8 lg:px-[5rem] lg:py-[3.125rem] min-h-[calc(100vh-80px)]">
|
||||
<section className="flex flex-col items-center justify-center p-4 space-y-12 sm:p-6 md:p-8 lg:px-[5rem] lg:py-[3.125rem] min-h-[calc(100vh-80px)]">
|
||||
<div className="text-center">
|
||||
<div className="text-5xl font-medium">
|
||||
Never forget anything, anywhere
|
||||
|
|
@ -262,7 +262,7 @@ export function LoginPage() {
|
|||
</LoginCard>
|
||||
) : (
|
||||
<LoginCard>
|
||||
<div className="w-full flex flex-col gap-4">
|
||||
<div className="w-full flex flex-col" style={{ gap: "12px" }}>
|
||||
{params.get("error") && (
|
||||
<div className="text-red-500">
|
||||
Error: {params.get("error")}. Please try again!
|
||||
|
|
@ -398,72 +398,79 @@ export function LoginPage() {
|
|||
) : null}
|
||||
</div>
|
||||
|
||||
<TextSeparator text="OR" />
|
||||
<TextSeparator text="OR" className={cn(dmSansClassName())} />
|
||||
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
|
||||
<LabeledInput
|
||||
error={error}
|
||||
inputPlaceholder="your@email.com"
|
||||
inputProps={{
|
||||
"aria-invalid": error ? "true" : "false",
|
||||
disabled: isLoading,
|
||||
id: "email",
|
||||
onChange: (e) => {
|
||||
setEmail(e.target.value);
|
||||
error && setError(null);
|
||||
},
|
||||
required: true,
|
||||
value: email,
|
||||
}}
|
||||
inputType="email"
|
||||
/>
|
||||
|
||||
<div className="relative">
|
||||
<Button
|
||||
className='flex justify-center items-center w-full h-[44px] relative gap-3 p-2 rounded-xl'
|
||||
style={{
|
||||
background:
|
||||
'linear-gradient(182.37deg, #0ff0d2 -91.53%, #5bd3fb -67.8%, #1e0ff0 95.17%)',
|
||||
boxShadow:
|
||||
'1px 1px 2px 0px #1A88FF inset, 0 2px 10px 0 rgba(5, 1, 0, 0.20)',
|
||||
<div className="flex flex-col gap-6">
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
|
||||
<LabeledInput
|
||||
error={error}
|
||||
inputPlaceholder="your@email.com"
|
||||
inputProps={{
|
||||
"aria-invalid": error ? "true" : "false",
|
||||
disabled: isLoading,
|
||||
id: "email",
|
||||
onChange: (e) => {
|
||||
setEmail(e.target.value);
|
||||
error && setError(null);
|
||||
},
|
||||
required: true,
|
||||
value: email,
|
||||
}}
|
||||
disabled={isLoading}
|
||||
type="submit"
|
||||
>
|
||||
<Logo className="size-4" />
|
||||
{isLoadingEmail
|
||||
? "Sending login link..."
|
||||
: "Log in with Supermemory"}
|
||||
</Button>
|
||||
{lastUsedMethod === "magic_link" && (
|
||||
<div className="absolute -top-2 -right-2">
|
||||
<Badge variant="default" className="text-xs">
|
||||
Last used
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
inputType="email"
|
||||
/>
|
||||
|
||||
<Label1Regular className={cn("text-center !text-xs text-[#737373B2]", dmSansClassName())}>
|
||||
By continuing, you agree to our{" "}
|
||||
<span className="inline-block">
|
||||
<a
|
||||
className="underline"
|
||||
href="https://supermemory.ai/terms-of-service"
|
||||
>
|
||||
Terms
|
||||
</a>{" "}
|
||||
and{" "}
|
||||
<a
|
||||
className="underline"
|
||||
href="https://supermemory.ai/privacy-policy"
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
</Label1Regular>
|
||||
<div className="relative">
|
||||
<Button
|
||||
className="flex justify-center items-center w-full h-[44px] relative gap-3 p-2 rounded-xl"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(182.37deg, #0ff0d2 -91.53%, #5bd3fb -67.8%, #1e0ff0 95.17%)",
|
||||
boxShadow:
|
||||
"1px 1px 2px 0px #1A88FF inset, 0 2px 10px 0 rgba(5, 1, 0, 0.20)",
|
||||
}}
|
||||
disabled={isLoading}
|
||||
type="submit"
|
||||
>
|
||||
<Logo className="size-4" />
|
||||
{isLoadingEmail
|
||||
? "Sending login link..."
|
||||
: "Log in with Supermemory"}
|
||||
</Button>
|
||||
{lastUsedMethod === "magic_link" && (
|
||||
<div className="absolute -top-2 -right-2">
|
||||
<Badge variant="default" className="text-xs">
|
||||
Last used
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<Label1Regular
|
||||
className={cn(
|
||||
"text-center !text-xs text-[#737373B2]",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
By continuing, you agree to our{" "}
|
||||
<span className="inline-block">
|
||||
<a
|
||||
className="underline"
|
||||
href="https://supermemory.ai/terms-of-service"
|
||||
>
|
||||
Terms
|
||||
</a>{" "}
|
||||
and{" "}
|
||||
<a
|
||||
className="underline"
|
||||
href="https://supermemory.ai/privacy-policy"
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
</Label1Regular>
|
||||
</div>
|
||||
</div>
|
||||
</LoginCard>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue