"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/company-brain onboarding is focused: just get Supermemory into Slack. // Coding agents live under "More tools (full catalog)". team: [ { id: "slack", label: "Slack", blurb: "Ask questions in-channel.", kind: "slack", recommended: true, }, ], 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" ? "Add Supermemory to Slack so your team can ask in any channel." : "Pick the tool you open every day — about 60 seconds."}

{tools.length > 1 ? ( <> { analytics.onboardingAgentSelected({ agent: id }) setSelected(id) }} />

{activeTool.blurb}

) : (

Set up {activeTool.label}

{activeTool.blurb}

)} {activeTool.kind === "slack" ? ( ) : activeTool.kind === "mcp" ? ( ) : activeTool.pluginId ? ( ) : null}
1 ? "justify-between" : "justify-end", )} > {tools.length > 1 && ( More tools (full catalog) )}
) } 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 FlowTabs({ tools, selected, onSelect, }: { tools: readonly FlowTool[] selected: FlowToolId onSelect: (id: FlowToolId) => void }) { return (
{tools.map((tool) => { const active = tool.id === selected return ( ) })}
) } function StepRow({ index, title, done, children, last, }: { index: number title: ReactNode done?: boolean children?: ReactNode last?: boolean }) { return (
{done ? : index} {!last && ( )}
{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 (
Add Supermemory to your Slack workspace {loading ? ( Checking… ) : ( Add to Slack )}
) } /> 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 ( ) }