"use client" import { useEffect, useMemo, useState } from "react" import type { ReactNode } from "react" import Image from "next/image" import Link from "next/link" import { Button } from "@ui/components/button" import { ArrowRight, Check, Copy, ExternalLink, Loader2, Plug, } from "lucide-react" import { cn } from "@lib/utils" import { dmSans125ClassName } from "@/lib/fonts" import { toast } from "sonner" import { PLUGIN_CATALOG } from "@/lib/plugin-catalog" import { analytics } from "@/lib/analytics" import type { BrainMode } from "./types" const BACKEND = process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai" const TEST_PROMPT = "What do we know about [topic]?" type FlowToolId = "slack" | "mcp" | "codex" | "claude-code" type FlowToolKind = "slack" | "mcp" | "plugin" type FlowTool = { id: FlowToolId label: string blurb: string kind: FlowToolKind pluginId?: string recommended?: boolean } const TOOL_OPTIONS: Record = { team: [ { id: "slack", label: "Slack", blurb: "Ask questions in-channel.", kind: "slack", recommended: true, }, { id: "claude-code", label: "Claude Code", blurb: "Shared context in your terminal.", kind: "plugin", pluginId: "claude_code", }, { id: "codex", label: "Codex", blurb: "OpenAI's coding agent.", kind: "plugin", pluginId: "codex", }, ], personal: [ { id: "mcp", label: "MCP", blurb: "Use the universal URL in any client.", kind: "mcp", recommended: true, }, { id: "codex", label: "Codex", blurb: "OpenAI's coding agent.", kind: "plugin", pluginId: "codex", }, { id: "claude-code", label: "Claude Code", blurb: "Context in your terminal.", kind: "plugin", pluginId: "claude_code", }, ], } const modalCardStyle = { boxShadow: "0 2.842px 14.211px 0 rgba(0, 0, 0, 0.25), 0.711px 0.711px 0.711px 0 rgba(255, 255, 255, 0.10) inset", } const inputBevelStyle = { boxShadow: "0px 1px 2px 0px rgba(0,43,87,0.1), inset 0px 0px 0px 1px rgba(43,49,67,0.08), inset 0px 1px 1px 0px rgba(0,0,0,0.08), inset 0px 2px 4px 0px rgba(0,0,0,0.02)", } interface Props { mode: BrainMode mcpUrl: string onContinue: () => void } export function StepIngest({ mode, mcpUrl, onContinue }: Props) { const tools = TOOL_OPTIONS[mode] const [selected, setSelected] = useState(tools[0].id) useEffect(() => { setSelected(tools[0].id) }, [tools]) const activeTool = useMemo( () => tools.find((t) => t.id === selected) ?? tools[0], [tools, selected], ) const handleContinue = () => { analytics.onboardingIngestCompleted() onContinue() } const handleSkip = () => { analytics.onboardingIngestSkipped() onContinue() } return (

Use your brain where you work

{mode === "team" ? "Pick where your team asks questions, then set it up." : "Pick the tool you open every day — about 60 seconds."}

{/* Left rail: pick a tool */}
{tools.map((tool) => ( { analytics.onboardingAgentSelected({ agent: tool.id }) setSelected(tool.id) }} /> ))} More tools (full catalog)
{/* Right pane: setup detail */}

Set up {activeTool.label}

{activeTool.blurb}

{activeTool.kind === "slack" ? ( ) : activeTool.kind === "mcp" ? ( ) : activeTool.pluginId ? ( ) : null}
) } function ToolIcon({ id, className }: { id: FlowToolId; className?: string }) { if (id === "slack") return if (id === "mcp") return const file = id === "claude-code" ? "claude" : id return ( ) } function FlowToolRow({ tool, active, onSelect, }: { tool: FlowTool active: boolean onSelect: () => void }) { return ( ) } function StepRow({ index, title, done, children, }: { index: number title: ReactNode done?: boolean children?: ReactNode }) { return (
{done ? : index}
{title}
{children ?
{children}
: null}
) } // Coding-agent plugins (Codex, Claude Code) auto-login via OAuth, so the // "Save your API key" step is dropped — we render the remaining install steps. function PluginSetup({ pluginId }: { pluginId: string }) { const plugin = PLUGIN_CATALOG[pluginId] const steps = (plugin?.installSteps ?? []).filter( (s) => !s.secret && !s.code?.includes("sm_..."), ) return (
{steps.map((step, i) => ( {step.description ? (

{step.description}

) : null} {step.code ? : null}
))}
) } function McpGenericSetup({ mcpUrl }: { mcpUrl: string }) { return (
Per-client setup guides
) } function SlackSetupPanel() { const [status, setStatus] = useState<{ connected: boolean teamName: string | null } | null>(null) const [loading, setLoading] = useState(true) useEffect(() => { let active = true ;(async () => { try { const res = await fetch(`${BACKEND}/brain/slack/status`, { credentials: "include", }) if (active && res.ok) { setStatus( (await res.json()) as { connected: boolean teamName: string | null }, ) } } finally { if (active) setLoading(false) } })() return () => { active = false } }, []) const connected = status?.connected ?? false return (
{!connected && (loading ? ( Checking… ) : ( ))} Mention @supermemory in{" "} #general } />
) } function CopyCodeBlock({ code }: { code: string }) { const [copied, setCopied] = useState(false) const copy = async () => { try { await navigator.clipboard.writeText(code) setCopied(true) toast.success("Copied") setTimeout(() => setCopied(false), 1500) } catch { toast.error("Could not copy") } } return (
				{code}
			
) } function McpUrlRow({ url }: { url: string }) { const [copied, setCopied] = useState(false) const copy = async () => { try { await navigator.clipboard.writeText(url) setCopied(true) toast.success("MCP URL copied") setTimeout(() => setCopied(false), 2000) } catch { toast.error("Could not copy") } } return (

Universal MCP URL

{url}

) } function SlackMark({ className }: { className?: string }) { return ( ) }