mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-28 05:25:33 +00:00
feat(web): overhaul integrations page with featured hero, category rails and MCP setup modal (#979)
- Rebuild the integrations catalog as horizontally-scrollable category rails (MCP, Plugins, Knowledge bases, Apps & extensions) with header chevrons - Add an auto-rotating Featured hero with problem-led headlines, backdrop imagery and a gradient FEATURED badge - Surface all 9 MCP clients as individual cards that open a setup modal styled like the plugin connect flow (reuses MCPSteps embedded variant) - Align MCP setup steps and the Claude Desktop timeline to the design system - Merge the Integrations and Connections settings tabs into one entry
This commit is contained in:
parent
31d416b04a
commit
9fa626cf17
4 changed files with 1918 additions and 474 deletions
|
|
@ -8,8 +8,9 @@ export default function SettingsIntegrationsPage() {
|
|||
const searchParams = useSearchParams()
|
||||
|
||||
useEffect(() => {
|
||||
const qs = searchParams.toString()
|
||||
router.replace(`/settings${qs ? `?${qs}` : ""}#integrations`)
|
||||
const params = new URLSearchParams(searchParams.toString())
|
||||
params.set("view", "integrations")
|
||||
router.replace(`/?${params.toString()}`)
|
||||
}, [router, searchParams])
|
||||
|
||||
return null
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -69,52 +69,76 @@ export function ClaudeDesktopManualTimeline({
|
|||
}: ClaudeDesktopManualTimelineProps) {
|
||||
const isDetail = variant === "detail"
|
||||
|
||||
const textMuted = isDetail ? "text-[#B0B0B0]" : "text-muted-foreground"
|
||||
const border = isDetail ? "border-[#3D434D]" : "border-border"
|
||||
const spine = isDetail ? "bg-[#7C8C9E]" : "bg-muted-foreground/40"
|
||||
const textMuted = isDetail ? "text-[#A1A1AA]" : "text-muted-foreground"
|
||||
const border = isDetail ? "border-white/[0.07]" : "border-border"
|
||||
const spine = isDetail ? "bg-white/[0.14]" : "bg-muted-foreground/40"
|
||||
const circleBorder = isDetail
|
||||
? "border-[#9CA8B8]"
|
||||
? "border-white/[0.12]"
|
||||
: "border-muted-foreground/50"
|
||||
const circleBg = isDetail ? "bg-[#080B0F]" : "bg-background"
|
||||
const codeBg = isDetail ? "bg-[#050608]" : "bg-muted"
|
||||
const imgBorder = isDetail ? "border-[#3D434D]" : "border-border"
|
||||
const screenshotBed = isDetail ? "bg-[#2A2D35]" : "bg-muted"
|
||||
const circleBg = isDetail ? "bg-[#0D121A]" : "bg-background"
|
||||
const codeBg = isDetail ? "bg-[#0B0E13]" : "bg-muted"
|
||||
const imgBorder = isDetail ? "border-white/[0.07]" : "border-border"
|
||||
const screenshotBed = isDetail ? "bg-[#0B0E13]" : "bg-muted"
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"space-y-0",
|
||||
isDetail
|
||||
? "rounded-2xl border border-[#3D434D] bg-[#080B0F] p-5 sm:p-6"
|
||||
? ""
|
||||
: "rounded-xl border border-border bg-muted/50 p-4 sm:p-5",
|
||||
)}
|
||||
>
|
||||
<div className="relative">
|
||||
<div
|
||||
className={cn(
|
||||
"absolute top-4.5 bottom-4.5 left-4.5 w-0.5 rounded-full",
|
||||
isDetail
|
||||
? "absolute top-3 bottom-3 left-[10.5px] w-px"
|
||||
: "absolute top-4.5 bottom-4.5 left-4.5 w-0.5 rounded-full",
|
||||
spine,
|
||||
)}
|
||||
aria-hidden
|
||||
/>
|
||||
|
||||
<ol className="relative m-0 list-none space-y-10">
|
||||
<ol className="relative m-0 list-none space-y-7">
|
||||
{STEPS.map((item) => (
|
||||
<li className="relative flex gap-4 sm:gap-5" key={item.step}>
|
||||
<div className="relative z-1 flex w-9 shrink-0 justify-center pt-0.5">
|
||||
<div
|
||||
className={cn(
|
||||
"flex size-9 items-center justify-center rounded-full border-2 text-sm font-semibold tabular-nums shadow-sm",
|
||||
circleBorder,
|
||||
circleBg,
|
||||
isDetail ? "text-[#FAFAFA]" : "text-foreground",
|
||||
)}
|
||||
>
|
||||
{item.step}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"relative z-1 flex shrink-0 justify-center pt-0.5",
|
||||
isDetail ? "w-[22px]" : "w-9",
|
||||
)}
|
||||
>
|
||||
{isDetail ? (
|
||||
<div
|
||||
className={cn(
|
||||
"flex size-[22px] items-center justify-center rounded-full bg-[#0D121A] text-[11px] font-semibold tabular-nums text-[#4BA0FA]",
|
||||
"shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]",
|
||||
)}
|
||||
>
|
||||
{item.step}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
"flex size-9 items-center justify-center rounded-full border-2 text-sm font-semibold tabular-nums shadow-sm",
|
||||
circleBorder,
|
||||
circleBg,
|
||||
"text-foreground",
|
||||
)}
|
||||
>
|
||||
{item.step}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 space-y-3">
|
||||
<p className={cn("text-sm leading-relaxed", textMuted)}>
|
||||
<p
|
||||
className={cn(
|
||||
isDetail
|
||||
? "text-[13px] leading-relaxed text-[#A1A1AA]"
|
||||
: cn("text-sm leading-relaxed", textMuted),
|
||||
)}
|
||||
>
|
||||
{item.body}
|
||||
</p>
|
||||
|
||||
|
|
@ -123,14 +147,14 @@ export function ClaudeDesktopManualTimeline({
|
|||
<div className="relative">
|
||||
<pre
|
||||
className={cn(
|
||||
"max-w-full overflow-x-auto rounded-lg border p-4 pr-12 text-xs",
|
||||
"max-w-full overflow-x-auto rounded-[10px] border p-4 pr-12 text-xs",
|
||||
border,
|
||||
codeBg,
|
||||
)}
|
||||
>
|
||||
<code
|
||||
className={cn(
|
||||
"block font-mono whitespace-pre-wrap break-all text-emerald-400",
|
||||
"block font-mono whitespace-pre-wrap break-all text-[#E4E4E7]",
|
||||
dmMonoClassName(),
|
||||
)}
|
||||
>
|
||||
|
|
@ -139,13 +163,13 @@ export function ClaudeDesktopManualTimeline({
|
|||
</pre>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-2 right-2 flex size-8 cursor-pointer items-center justify-center rounded-md border border-[#3D434D] bg-[#121820] hover:bg-[#1a2230]"
|
||||
className="absolute top-2 right-2 flex size-7 cursor-pointer items-center justify-center rounded-full bg-[#0D121A] shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.6)] transition-opacity hover:opacity-80"
|
||||
onClick={onCopySnippet}
|
||||
>
|
||||
{snippetCopied ? (
|
||||
<span className="text-xs text-emerald-500">✓</span>
|
||||
<CheckIcon className="size-3.5 text-[#4BA0FA]" />
|
||||
) : (
|
||||
<CopyIcon className="size-3.5 text-[#8B8B8B] hover:text-white" />
|
||||
<CopyIcon className="size-3.5 text-[#737373]" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,67 @@ import {
|
|||
getManualInstallEntry,
|
||||
} from "@/lib/mcp-manual-instructions"
|
||||
import { ClaudeDesktopManualTimeline } from "@/components/mcp-modal/claude-desktop-manual-timeline"
|
||||
import { INSET } from "@/components/integrations/install-steps"
|
||||
|
||||
function McpCodeBlock({
|
||||
code,
|
||||
multiline,
|
||||
onCopy,
|
||||
}: {
|
||||
code: string
|
||||
multiline?: boolean
|
||||
onCopy?: () => void
|
||||
}) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const copy = () => {
|
||||
navigator.clipboard.writeText(code)
|
||||
setCopied(true)
|
||||
toast.success("Copied to clipboard!")
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
onCopy?.()
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group flex min-w-0 gap-2 rounded-[10px] border border-white/[0.07] bg-[#0B0E13] px-3 py-2.5",
|
||||
multiline ? "items-start" : "items-center",
|
||||
)}
|
||||
>
|
||||
<div className="relative min-w-0 flex-1">
|
||||
<pre
|
||||
className={cn(
|
||||
"scrollbar-none min-w-0 overflow-x-auto font-mono text-[12px] leading-[1.6] text-[#E4E4E7]",
|
||||
multiline ? "whitespace-pre-wrap break-all" : "whitespace-pre pr-4",
|
||||
dmMonoClassName(),
|
||||
)}
|
||||
>
|
||||
{code}
|
||||
</pre>
|
||||
{!multiline && (
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-y-0 right-0 w-8 bg-gradient-to-l from-[#0B0E13] to-transparent"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Copy"
|
||||
onClick={copy}
|
||||
className={cn(
|
||||
"flex size-7 shrink-0 items-center justify-center rounded-full bg-[#0D121A] transition-opacity hover:opacity-80",
|
||||
INSET,
|
||||
)}
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="size-3.5 text-[#4BA0FA]" />
|
||||
) : (
|
||||
<CopyIcon className="size-3.5 text-[#737373]" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const clients = {
|
||||
chatgpt: "ChatGPT",
|
||||
|
|
@ -101,7 +162,8 @@ function ClientToolIcon({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"size-10 shrink-0 rounded-[10px] bg-[#0D121A] border border-[#242A33] flex items-center justify-center overflow-hidden",
|
||||
"size-10 shrink-0 rounded-[10px] bg-[#080B0F] flex items-center justify-center overflow-hidden",
|
||||
"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.6)]",
|
||||
)}
|
||||
>
|
||||
{failed ? (
|
||||
|
|
@ -137,7 +199,6 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
"mcpTab",
|
||||
parseAsStringLiteral(["oneClick", "manual"] as const).withDefault("manual"),
|
||||
)
|
||||
const [isCommandCopied, setIsCommandCopied] = useState(false)
|
||||
const [isManualCopied, setIsManualCopied] = useState(false)
|
||||
const [activeStep, setActiveStep] = useQueryState(
|
||||
"mcpStep",
|
||||
|
|
@ -170,15 +231,6 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
return command
|
||||
}
|
||||
|
||||
const copyToClipboard = () => {
|
||||
const command = generateInstallCommand()
|
||||
navigator.clipboard.writeText(command)
|
||||
analytics.mcpInstallCmdCopied()
|
||||
setIsCommandCopied(true)
|
||||
setActiveStep(3)
|
||||
setTimeout(() => setIsCommandCopied(false), 2000)
|
||||
}
|
||||
|
||||
const copyManualSnippet = (text: string) => {
|
||||
navigator.clipboard.writeText(text)
|
||||
analytics.mcpInstallCmdCopied()
|
||||
|
|
@ -203,17 +255,6 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
? "manual"
|
||||
: setupTab
|
||||
|
||||
const gradientStep3 =
|
||||
activeStep === 3
|
||||
? {
|
||||
background:
|
||||
"linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
|
||||
backgroundClip: "text",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
}
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
|
@ -228,11 +269,11 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
key={category.label}
|
||||
className={cn(
|
||||
"flex flex-col gap-4 py-5 sm:flex-row sm:items-start sm:gap-8",
|
||||
rowIndex > 0 && "border-t border-[#242A33]",
|
||||
rowIndex > 0 && "border-t border-white/[0.06]",
|
||||
)}
|
||||
>
|
||||
<div className="w-full shrink-0 sm:w-30">
|
||||
<p className="text-[13px] font-semibold tracking-[-0.01em] text-[#8B8B8B]">
|
||||
<p className="text-[13px] font-semibold tracking-[-0.01em] text-[#A1A1AA]">
|
||||
{category.label}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -249,9 +290,9 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
setActiveStep(2)
|
||||
}}
|
||||
className={cn(
|
||||
"group flex w-full min-w-0 items-center gap-3 rounded-xl border border-[#242A33] bg-[#080B0F] p-3.5 text-left transition-colors",
|
||||
"hover:border-[#3273FC4D] hover:bg-[#08142D]",
|
||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#3273FC]/40",
|
||||
"group flex w-full min-w-0 items-center gap-3 rounded-[12px] bg-[#0D121A] p-3.5 text-left transition-colors",
|
||||
"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.5)] hover:bg-[#10151D]",
|
||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#4BA0FA]/40",
|
||||
)}
|
||||
>
|
||||
<ClientToolIcon clientKey={item.key} title={item.title} />
|
||||
|
|
@ -259,10 +300,12 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
<p className="text-sm font-medium text-[#FAFAFA]">
|
||||
{item.title}
|
||||
</p>
|
||||
<p className="text-xs text-[#737373]">{item.subtitle}</p>
|
||||
<p className="text-[12px] text-[#737373]">
|
||||
{item.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
<ChevronRight
|
||||
className="size-4 shrink-0 text-[#525866] transition-transform group-hover:translate-x-0.5"
|
||||
className="size-4 shrink-0 text-[#525D6E] transition-transform group-hover:translate-x-0.5"
|
||||
aria-hidden
|
||||
/>
|
||||
</button>
|
||||
|
|
@ -270,51 +313,63 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
</div>
|
||||
</div>
|
||||
))}
|
||||
<p className="pt-1 text-sm text-[#8B8B8B]">
|
||||
<p className="pt-1 text-[13px] text-[#A1A1AA]">
|
||||
You can connect several clients; setup is different for each one.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className={cn("space-y-4", dmSansClassName())}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setSelectedClient(null)
|
||||
setActiveStep(1)
|
||||
}}
|
||||
className="text-sm font-medium text-[#8B8B8B] transition-colors hover:text-[#FAFAFA]"
|
||||
{!isEmbedded && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setSelectedClient(null)
|
||||
setActiveStep(1)
|
||||
}}
|
||||
className="text-sm font-medium text-[#A1A1AA] transition-colors hover:text-[#FAFAFA]"
|
||||
>
|
||||
← All clients
|
||||
</button>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<ClientToolIcon
|
||||
clientKey={selectedKey}
|
||||
title={clients[selectedKey]}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-lg font-medium text-[#FAFAFA]">
|
||||
{clients[selectedKey]}
|
||||
</p>
|
||||
<p className="text-sm text-[#737373]">
|
||||
{detailSetup ? setupInstructionsSubtitle(detailSetup) : ""}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"space-y-4",
|
||||
!isEmbedded && "border-t border-white/[0.06] pt-8",
|
||||
)}
|
||||
>
|
||||
← All clients
|
||||
</button>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<ClientToolIcon
|
||||
clientKey={selectedKey}
|
||||
title={clients[selectedKey]}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-lg font-medium text-[#FAFAFA]">
|
||||
{clients[selectedKey]}
|
||||
</p>
|
||||
<p className="text-sm text-[#737373]">
|
||||
{detailSetup ? setupInstructionsSubtitle(detailSetup) : ""}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 border-t border-[#242A33] pt-8">
|
||||
{detailSetup && mcpClientSetupShowsTabs(detailSetup) ? (
|
||||
<div
|
||||
className="flex w-full max-w-md flex-row gap-1 rounded-full border border-[#3D434D] bg-[#0D121A] p-1"
|
||||
className={cn(
|
||||
"flex w-full flex-row gap-0.5 rounded-full bg-[#0D121A] p-0.5",
|
||||
"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.5)]",
|
||||
)}
|
||||
role="tablist"
|
||||
aria-label="Setup method"
|
||||
>
|
||||
<button
|
||||
className={cn(
|
||||
"min-h-9 flex-1 rounded-full px-3 py-2 text-center text-xs font-medium transition-all",
|
||||
"min-h-8 flex-1 rounded-full px-3 text-center text-[12px] font-medium transition-colors",
|
||||
effectiveSetupTab === "manual"
|
||||
? "border border-[#3D434D] bg-[#080B0F] text-white"
|
||||
: "text-[#8B8B8B] hover:text-white",
|
||||
? "bg-white/[0.10] text-[#FAFAFA]"
|
||||
: "text-[#A1A1AA] hover:text-[#FAFAFA]",
|
||||
)}
|
||||
onClick={() => setSetupTab("manual")}
|
||||
role="tab"
|
||||
|
|
@ -325,10 +380,10 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
</button>
|
||||
<button
|
||||
className={cn(
|
||||
"min-h-9 flex-1 rounded-full px-3 py-2 text-center text-xs font-medium transition-all",
|
||||
"min-h-8 flex-1 rounded-full px-3 text-center text-[12px] font-medium transition-colors",
|
||||
effectiveSetupTab === "oneClick"
|
||||
? "border border-[#3D434D] bg-[#080B0F] text-white"
|
||||
: "text-[#8B8B8B] hover:text-white",
|
||||
? "bg-white/[0.10] text-[#FAFAFA]"
|
||||
: "text-[#A1A1AA] hover:text-[#FAFAFA]",
|
||||
)}
|
||||
onClick={() => setSetupTab("oneClick")}
|
||||
role="tab"
|
||||
|
|
@ -345,47 +400,31 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
<>
|
||||
{selectedClient === "mcp-url" && (
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-[#8B8B8B]">
|
||||
<p className="text-[13px] leading-relaxed text-[#A1A1AA]">
|
||||
Paste this URL into any MCP client that supports remote
|
||||
servers over HTTPS.
|
||||
</p>
|
||||
<div className="relative">
|
||||
<input
|
||||
className={cn(
|
||||
"w-full rounded-lg border border-[#3D434D] bg-black p-2 pr-10 font-mono text-xs text-emerald-400",
|
||||
dmMonoClassName(),
|
||||
)}
|
||||
readOnly
|
||||
value="https://mcp.supermemory.ai/mcp"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-1 right-1 cursor-pointer p-1"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
"https://mcp.supermemory.ai/mcp",
|
||||
)
|
||||
analytics.mcpInstallCmdCopied()
|
||||
toast.success("Copied to clipboard!")
|
||||
setActiveStep(3)
|
||||
}}
|
||||
>
|
||||
<CopyIcon className="size-4 text-[#8B8B8B] hover:text-white" />
|
||||
</button>
|
||||
</div>
|
||||
<McpCodeBlock
|
||||
code="https://mcp.supermemory.ai/mcp"
|
||||
onCopy={() => {
|
||||
analytics.mcpInstallCmdCopied()
|
||||
toast.success("Copied to clipboard!")
|
||||
setActiveStep(3)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{selectedClient === "cursor" && (
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm text-[#8B8B8B]">
|
||||
<p className="text-[13px] leading-relaxed text-[#A1A1AA]">
|
||||
Open the link below to add supermemory in Cursor, or use
|
||||
Manual instructions to edit{" "}
|
||||
<code className="text-xs text-emerald-400/90">
|
||||
<code className="font-mono text-[12px] text-[#8BC6FF]">
|
||||
~/.cursor/mcp.json
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<div className="flex flex-col items-center gap-4 rounded-xl border border-[#1e3a2f] bg-[#0a1510] p-6">
|
||||
<div className="flex flex-col items-center gap-4 rounded-[10px] border border-white/[0.07] bg-[#0B0E13] p-6">
|
||||
<a
|
||||
href={getCursorDeeplink()}
|
||||
onClick={() => {
|
||||
|
|
@ -411,50 +450,18 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
{selectedClient !== "mcp-url" &&
|
||||
selectedClient !== "cursor" && (
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-[#8B8B8B]">
|
||||
<p className="text-[13px] leading-relaxed text-[#A1A1AA]">
|
||||
Run this command in your terminal. It installs the MCP
|
||||
for {clients[selectedKey]} and starts OAuth when
|
||||
needed.
|
||||
</p>
|
||||
<div className="relative">
|
||||
<input
|
||||
className={cn(
|
||||
"w-full overflow-hidden text-ellipsis whitespace-nowrap rounded-xl py-4 pr-28 pl-3 text-xs text-white",
|
||||
dmMonoClassName(),
|
||||
)}
|
||||
style={{
|
||||
border: "1px solid rgba(61, 67, 77, 0.10)",
|
||||
background: "#0D121A",
|
||||
}}
|
||||
readOnly
|
||||
value={generateInstallCommand()}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"absolute top-[5px] right-1 flex cursor-pointer items-center gap-2 rounded-[10px] px-3 py-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}
|
||||
>
|
||||
{isCommandCopied ? (
|
||||
<>
|
||||
<Check className="size-4 text-white" />
|
||||
<span className="text-white">Copied</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CopyIcon className="size-5 text-white stroke-[2px]" />
|
||||
<span className="text-white">Copy</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<McpCodeBlock
|
||||
code={generateInstallCommand()}
|
||||
onCopy={() => {
|
||||
analytics.mcpInstallCmdCopied()
|
||||
setActiveStep(3)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
@ -464,7 +471,7 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
if (manual.kind === "chatgpt") {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<ol className="list-decimal space-y-2 pl-5 text-sm text-[#8B8B8B]">
|
||||
<ol className="list-decimal space-y-2 pl-5 text-[13px] leading-relaxed text-[#A1A1AA]">
|
||||
<li>Open ChatGPT in your browser.</li>
|
||||
<li>
|
||||
Go to Settings → Apps → Advanced settings → enable
|
||||
|
|
@ -478,27 +485,15 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
Paste the URL below and complete OAuth in ChatGPT.
|
||||
</li>
|
||||
</ol>
|
||||
<div className="relative">
|
||||
<input
|
||||
className={cn(
|
||||
"w-full rounded-lg border border-[#3D434D] bg-black p-2 pr-10 font-mono text-xs text-emerald-400",
|
||||
dmMonoClassName(),
|
||||
)}
|
||||
readOnly
|
||||
value={CHATGPT_REMOTE_MCP_URL}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-1 right-1 cursor-pointer p-1"
|
||||
onClick={() =>
|
||||
copyManualSnippet(CHATGPT_REMOTE_MCP_URL)
|
||||
}
|
||||
>
|
||||
<CopyIcon className="size-4 text-[#8B8B8B] hover:text-white" />
|
||||
</button>
|
||||
</div>
|
||||
<McpCodeBlock
|
||||
code={CHATGPT_REMOTE_MCP_URL}
|
||||
onCopy={() => {
|
||||
analytics.mcpInstallCmdCopied()
|
||||
setActiveStep(3)
|
||||
}}
|
||||
/>
|
||||
<a
|
||||
className="inline-block text-sm text-[#4BA0FA] underline hover:text-[#6BB8FF]"
|
||||
className="inline-block text-[13px] text-[#4BA0FA] underline hover:text-[#8BC6FF]"
|
||||
href="https://developers.openai.com/api/docs/guides/developer-mode/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
|
|
@ -523,36 +518,21 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
const snippet = buildMcpUrlRemoteJson("your-api-key-here")
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-[#8B8B8B]">
|
||||
<p className="text-[13px] leading-relaxed text-[#A1A1AA]">
|
||||
Add this to your client's MCP config. Replace the
|
||||
placeholder with an API key from supermemory settings
|
||||
(Integrations).
|
||||
</p>
|
||||
<div className="relative">
|
||||
<pre className="max-w-full overflow-x-auto rounded-lg border border-[#3D434D] bg-black p-4 pr-12 text-xs">
|
||||
<code
|
||||
className={cn(
|
||||
"block font-mono whitespace-pre-wrap break-all text-emerald-400",
|
||||
dmMonoClassName(),
|
||||
)}
|
||||
>
|
||||
{snippet}
|
||||
</code>
|
||||
</pre>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-2 right-2 flex size-8 cursor-pointer items-center justify-center rounded bg-[#0D121A] hover:bg-[#1a1a1a]"
|
||||
onClick={() => copyManualSnippet(snippet)}
|
||||
>
|
||||
{isManualCopied ? (
|
||||
<span className="text-emerald-500">✓</span>
|
||||
) : (
|
||||
<CopyIcon className="size-3.5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<McpCodeBlock
|
||||
code={snippet}
|
||||
multiline
|
||||
onCopy={() => {
|
||||
analytics.mcpInstallCmdCopied()
|
||||
setActiveStep(3)
|
||||
}}
|
||||
/>
|
||||
{detailSetup?.oneClick ? (
|
||||
<p className="text-xs text-[#737373]">
|
||||
<p className="text-[12px] text-[#737373]">
|
||||
Use Bearer auth in headers, or switch to One click
|
||||
setup and paste the HTTPS URL if your client
|
||||
supports OAuth only.
|
||||
|
|
@ -563,35 +543,22 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
}
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-[#8B8B8B]">{manual.paths}</p>
|
||||
<p className="text-sm text-[#8B8B8B]">
|
||||
<p className="text-[13px] leading-relaxed text-[#A1A1AA]">
|
||||
{manual.paths}
|
||||
</p>
|
||||
<p className="text-[13px] leading-relaxed text-[#A1A1AA]">
|
||||
Merge the snippet with your existing file (create it if
|
||||
needed). Restart the client and complete OAuth when
|
||||
prompted.
|
||||
</p>
|
||||
<div className="relative">
|
||||
<pre className="max-w-full overflow-x-auto rounded-lg border border-[#3D434D] bg-black p-4 pr-12 text-xs">
|
||||
<code
|
||||
className={cn(
|
||||
"block font-mono whitespace-pre-wrap break-all text-emerald-400",
|
||||
dmMonoClassName(),
|
||||
)}
|
||||
>
|
||||
{manual.snippet}
|
||||
</code>
|
||||
</pre>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-2 right-2 flex size-8 cursor-pointer items-center justify-center rounded bg-[#0D121A] hover:bg-[#1a1a1a]"
|
||||
onClick={() => copyManualSnippet(manual.snippet)}
|
||||
>
|
||||
{isManualCopied ? (
|
||||
<span className="text-emerald-500">✓</span>
|
||||
) : (
|
||||
<CopyIcon className="size-3.5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<McpCodeBlock
|
||||
code={manual.snippet}
|
||||
multiline
|
||||
onCopy={() => {
|
||||
analytics.mcpInstallCmdCopied()
|
||||
setActiveStep(3)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})()
|
||||
|
|
@ -599,47 +566,47 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-[#242A33] pt-8">
|
||||
<h3
|
||||
className="mb-4 text-lg font-medium text-white"
|
||||
style={gradientStep3}
|
||||
>
|
||||
{detailSetup != null &&
|
||||
mcpClientShowsManual(detailSetup, effectiveSetupTab)
|
||||
? selectedClient === "chatgpt"
|
||||
? "Finish in ChatGPT"
|
||||
: selectedClient === "claude"
|
||||
? "Finish in Claude Desktop"
|
||||
: "Save and restart"
|
||||
: selectedClient === "cursor"
|
||||
? "Finish in Cursor"
|
||||
: "Run in terminal"}
|
||||
</h3>
|
||||
{activeStep === 3 && (
|
||||
<p
|
||||
{!isEmbedded && (
|
||||
<div className="border-t border-white/[0.06] pt-6">
|
||||
<h3
|
||||
className={cn(
|
||||
"flex items-center gap-2 rounded-xl p-4 pl-3 font-mono text-xs text-white",
|
||||
dmMonoClassName(),
|
||||
dmSansClassName(),
|
||||
"mb-3 text-[15px] font-semibold tracking-[-0.01em] text-[#FAFAFA]",
|
||||
)}
|
||||
style={{
|
||||
border: "1px solid rgba(61, 67, 77, 0.10)",
|
||||
background: "#0D121A",
|
||||
}}
|
||||
>
|
||||
<SyncLogoIcon className="size-4" />
|
||||
{detailSetup != null &&
|
||||
mcpClientShowsManual(detailSetup, effectiveSetupTab)
|
||||
? selectedClient === "chatgpt"
|
||||
? "Complete app setup in ChatGPT, then enable it for your chat."
|
||||
? "Finish in ChatGPT"
|
||||
: selectedClient === "claude"
|
||||
? "After Connectors, complete any prompts so supermemory is enabled for chat."
|
||||
: "Save your config, restart the client, and sign in if prompted."
|
||||
? "Finish in Claude Desktop"
|
||||
: "Save and restart"
|
||||
: selectedClient === "cursor"
|
||||
? "Complete the install in Cursor and sign in with OAuth if asked."
|
||||
: "Waiting for installation"}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
? "Finish in Cursor"
|
||||
: "Run in terminal"}
|
||||
</h3>
|
||||
{activeStep === 3 && (
|
||||
<p
|
||||
className={cn(
|
||||
dmSansClassName(),
|
||||
"flex items-center gap-2 rounded-[10px] border border-white/[0.07] bg-[#0B0E13] p-3 text-[12px] text-[#A1A1AA]",
|
||||
)}
|
||||
>
|
||||
<SyncLogoIcon className="size-4 shrink-0 text-[#4BA0FA]" />
|
||||
{detailSetup != null &&
|
||||
mcpClientShowsManual(detailSetup, effectiveSetupTab)
|
||||
? selectedClient === "chatgpt"
|
||||
? "Complete app setup in ChatGPT, then enable it for your chat."
|
||||
: selectedClient === "claude"
|
||||
? "After Connectors, complete any prompts so supermemory is enabled for chat."
|
||||
: "Save your config, restart the client, and sign in if prompted."
|
||||
: selectedClient === "cursor"
|
||||
? "Complete the install in Cursor and sign in with OAuth if asked."
|
||||
: "Waiting for installation"}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue