"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 (
{children}
) } export function FullScreenMessage({ title, subtitle, }: { title: string subtitle: string }) { return (

{title}

{subtitle}

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