added all the fixes around onboarding, dashboard

This commit is contained in:
Mahesh Sanikommmu 2025-11-19 18:52:53 -08:00
parent af6ab08c53
commit ed6b6fb2fc
20 changed files with 505 additions and 211 deletions

View file

@ -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,

View file

@ -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>

View file

@ -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 }}

View file

@ -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>

View file

@ -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>

View file

@ -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={{

View file

@ -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,

View file

@ -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>
)
}

View 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>
)
}

View file

@ -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>

View file

@ -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 {

View file

@ -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>
)}

View file

@ -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(

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -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,
)
}

View file

@ -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}
>

View file

@ -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}

View file

@ -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>
)}