"use client"
import { dmSans125ClassName } from "@/lib/fonts"
import { cn } from "@lib/utils"
import { ClaudeDesktopIcon, MCPIcon } from "@ui/assets/icons"
import { Logo, LogoFull } from "@ui/assets/Logo"
import { Popover, PopoverAnchor, PopoverContent } from "@ui/components/popover"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@ui/components/select"
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@ui/components/tooltip"
import { ArrowLeft, BadgeCheck, Check, LoaderIcon, X } from "lucide-react"
import { AnimatePresence, motion } from "motion/react"
import {
type ComponentType,
type ReactNode,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react"
type IconComponent = ComponentType<{ className?: string }>
// Mirror of the OAuth plugins in mono's packages/lib/plugins.ts. An entry here
// makes a client "verified" — we show its bundled icon + real name. Unknown DCR
// clients still connect; they just render as a generic MCP client.
export const OAUTH_PLUGINS: Record<
string,
{ name: string; icon?: IconComponent }
> = {
"supermemory-claude-code": { name: "Claude Code", icon: ClaudeDesktopIcon },
"supermemory-opencode": { name: "OpenCode" },
"supermemory-openclaw": { name: "OpenClaw" },
"supermemory-codex": { name: "OpenAI Codex" },
}
const GRADIENT_BG =
"linear-gradient(182.37deg, #0ff0d2 -91.53%, #5bd3fb -67.8%, #1e0ff0 95.17%)"
const GRADIENT_SHADOW =
"1px 1px 2px 0px #1A88FF inset, 0 2px 10px 0 rgba(5, 1, 0, 0.20)"
const EXPIRY_OPTIONS = [
{ label: "1 year", value: "365" },
{ label: "6 months", value: "180" },
{ label: "90 days", value: "90" },
{ label: "30 days", value: "30" },
{ label: "7 days", value: "7" },
{ label: "Never", value: "0" },
]
export function shortClientId(id: string): string {
return id.length > 12 ? `${id.slice(0, 4)}…${id.slice(-4)}` : id
}
export type ConsentOrg = {
id: string
name: string
}
export type ConsentPermission = "read" | "write"
export type ConsentScopeType = "full" | "scoped"
export type ConsentScope = {
permission: ConsentPermission
scopeType: ConsentScopeType
tags: string[]
expiresDays: number
}
function SectionLabel({ children }: { children: ReactNode }) {
return (
{children}
)
}
function ConnectingDots() {
return (
{["a", "b", "c"].map((k, i) => (
))}
)
}
function ConnectingHeader({
clientIcon: ClientIcon,
}: {
clientIcon: IconComponent
}) {
return (
)
}
function SpacesPicker({
options,
selected,
setSelected,
loading,
disabled,
}: {
options: string[]
selected: string[]
setSelected: (next: string[]) => void
loading: boolean
disabled?: boolean
}) {
const [query, setQuery] = useState("")
const [open, setOpen] = useState(false)
const fieldRef = useRef(null)
const filtered = useMemo(() => {
const q = query.trim().toLowerCase()
return options
.filter(
(o) => !selected.includes(o) && (!q || o.toLowerCase().includes(q)),
)
.slice(0, 50)
}, [options, selected, query])
const add = (t: string) => {
if (!selected.includes(t)) setSelected([...selected, t])
setQuery("")
}
const remove = (t: string) => setSelected(selected.filter((x) => x !== t))
return (
{selected.length > 0 && (
{selected.map((t) => (
{t}
))}
)}
setQuery(e.target.value)}
onFocus={() => setOpen(true)}
placeholder={loading ? "Loading spaces…" : "Search spaces to add…"}
value={query}
/>
{
if (fieldRef.current?.contains(e.target as Node)) e.preventDefault()
}}
onInteractOutside={(e) => {
if (fieldRef.current?.contains(e.target as Node)) e.preventDefault()
}}
onOpenAutoFocus={(e) => e.preventDefault()}
sideOffset={6}
>
{filtered.length === 0 ? (
{loading
? "Loading spaces…"
: query.trim()
? `No spaces match “${query.trim()}”.`
: "No spaces available."}
) : (
filtered.map((t) => (
))
)}
)
}
export function CardShell({ children }: { children: ReactNode }) {
return (
)
}
export function FullScreenMessage({
title,
subtitle,
}: {
title: string
subtitle: string
}) {
return (
)
}
export interface ConsentCardProps {
appLabel: string
verified: boolean
clientId: string
userEmail?: string
orgs: ConsentOrg[]
availableTags: string[]
tagsLoading: boolean
submitting: "approve" | "deny" | null
error: string | null
onEnterOrg: (orgId: string) => Promise
onScopedOpen: () => void
onSubmit: (accept: boolean, scope: ConsentScope) => void
onSignOut: () => void
}
export function ConsentCard({
appLabel,
verified,
clientId,
userEmail,
orgs,
availableTags,
tagsLoading,
submitting,
error,
onEnterOrg,
onScopedOpen,
onSubmit,
onSignOut,
}: ConsentCardProps) {
const [step, setStep] = useState<1 | 2>(1)
const [selectedOrgId, setSelectedOrgId] = useState(null)
const [switchingOrgId, setSwitchingOrgId] = useState(null)
const [autoTried, setAutoTried] = useState(false)
const [permission, setPermission] = useState("write")
const [scopeType, setScopeType] = useState("full")
const [tags, setTags] = useState([])
const [expiresDays, setExpiresDays] = useState("365")
const multiOrg = orgs.length > 1
const busy = submitting !== null || switchingOrgId !== null
const selectedOrg = orgs.find((o) => o.id === selectedOrgId) ?? null
const ClientIcon = (clientId && OAUTH_PLUGINS[clientId]?.icon) || MCPIcon
const title = verified ? `Authorize ${appLabel}` : "Authorize MCP"
const connectLabel = verified ? appLabel : "this MCP client"
const enterOrg = useCallback(
async (orgId: string) => {
if (!orgId) return
setSelectedOrgId(orgId)
setSwitchingOrgId(orgId)
try {
await onEnterOrg(orgId)
} catch {
setSwitchingOrgId(null)
return
}
setSwitchingOrgId(null)
setTags([])
setStep(2)
},
[onEnterOrg],
)
useEffect(() => {
if (autoTried || step !== 1 || orgs.length !== 1) return
setAutoTried(true)
void enterOrg(orgs[0]?.id ?? "")
}, [orgs, step, autoTried, enterOrg])
useEffect(() => {
if (step === 2 && scopeType === "scoped") onScopedOpen()
}, [step, scopeType, onScopedOpen])
const listRef = useRef(null)
const [canScrollUp, setCanScrollUp] = useState(false)
const [canScrollDown, setCanScrollDown] = useState(false)
const measureFades = useCallback((el: HTMLDivElement | null) => {
if (!el) return
setCanScrollUp(el.scrollTop > 8)
setCanScrollDown(el.scrollTop + el.clientHeight < el.scrollHeight - 8)
}, [])
useEffect(() => {
if (step !== 1 || orgs.length === 0) {
setCanScrollUp(false)
setCanScrollDown(false)
return
}
measureFades(listRef.current)
}, [orgs, step, measureFades])
const submit = (accept: boolean) =>
onSubmit(accept, {
permission,
scopeType,
tags,
expiresDays: Number(expiresDays),
})
return (
{step === 1 ? (
Select an organization
Choose which organization to connect {connectLabel} to.
measureFades(e.currentTarget)}
ref={listRef}
>
{orgs.length === 0 ? (
No organizations found on your account.
) : (
orgs.map((o) => {
const switching = switchingOrgId === o.id
return (
)
})
)}
{error && (
{error}
)}
{userEmail && (
Signed in as {userEmail}
)}
) : (
{title}
{verified && (
Verified app
)}
{verified ? appLabel : "An MCP client"} wants to connect to
your supermemory.
Connecting to
{selectedOrg?.name ?? "Workspace"}
{multiOrg && (
)}
Permission
Access
{scopeType === "scoped" && (
{tags.length > 0
? `${tags.length} space${tags.length === 1 ? "" : "s"} selected`
: "Type to search and add spaces."}
)}
Expires
{error &&
{error}
}
{clientId && !verified && (
App ID · {shortClientId(clientId)}
)}
)}
)
}
function Choice({
options,
value,
onChange,
disabled,
}: {
options: { value: T; label: string }[]
value: T
onChange: (v: T) => void
disabled?: boolean
}) {
return (
{options.map((o) => {
const active = o.value === value
return (
)
})}
)
}