From c71acbe8aed04052a94454ce5aaacf27bca80e03 Mon Sep 17 00:00:00 2001 From: ved015 <122012786+ved015@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:32:00 +0000 Subject: [PATCH 1/3] Make integrations page public for guests (#1073) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Allow logged-out users to load `/?view=integrations` without being redirected to login - Add a public integrations header with Supermemory branding and a login CTA - Run IntegrationsView in guest mode with static catalog data only, no auth-backed queries - Route all guest Connect actions to login with a redirect back to the integrations page Screenshot 2026-06-09 at 9 32 33 PM --- apps/web/app/(app)/page.tsx | 7 ++- apps/web/components/ensure-workspace.tsx | 11 ++-- apps/web/components/header.tsx | 36 ++++++++++- apps/web/components/integrations-view.tsx | 74 +++++++++++++++++++---- apps/web/middleware.ts | 5 ++ 5 files changed, 113 insertions(+), 20 deletions(-) diff --git a/apps/web/app/(app)/page.tsx b/apps/web/app/(app)/page.tsx index 82948b2a5..cd41476a6 100644 --- a/apps/web/app/(app)/page.tsx +++ b/apps/web/app/(app)/page.tsx @@ -582,6 +582,7 @@ export default function NewPage() { viewMode === "dashboard" || (viewMode === "graph" && isMobile) const isGraphMode = viewMode === "graph" const showBottomNav = isMobile && !!session && !isChatView + const isPublicIntegrations = !session && viewMode === "integrations" return ( @@ -608,7 +609,9 @@ export default function NewPage() { /> )} - {!session && viewMode === "mcp" ? ( + {isPublicIntegrations ? ( + + ) : !session && viewMode === "mcp" ? ( ) : (
) : viewMode === "integrations" ? (
- +
) : viewMode === "mcp" ? ( { - if (isMcpPublicPage) return + if (isGuestPublicAppPage) return if (isRestoring) return if (!session) { router.replace( @@ -44,11 +47,11 @@ export function EnsureWorkspace({ children }: { children: React.ReactNode }) { isRestoring, isOnboarding, router, - isMcpPublicPage, + isGuestPublicAppPage, ]) const showLoading = - !isMcpPublicPage && + !isGuestPublicAppPage && (isRestoring || (!session && !isRestoring) || (session && organizations === null) || diff --git a/apps/web/components/header.tsx b/apps/web/components/header.tsx index cc3afb71e..e47f56c26 100644 --- a/apps/web/components/header.tsx +++ b/apps/web/components/header.tsx @@ -504,7 +504,41 @@ export function Header({ onAddMemory, onOpenSearch }: HeaderProps) { ) } -export function PublicHeader() { +export function PublicHeader({ + variant = "default", +}: { + variant?: "default" | "integrations" +}) { + if (variant === "integrations") { + return ( +
+ + +

+ supermemory +

+ + + + + +
+ ) + } + return (
(null) const [connectingProvider, setConnectingProvider] = @@ -1493,6 +1498,7 @@ export function IntegrationsView() { if (!res.ok) throw new Error("Failed to fetch plugins") return (await res.json()) as { plugins: string[] } }, + enabled: !publicMode, queryKey: ["plugins"], }) @@ -1507,7 +1513,7 @@ export function IntegrationsView() { return response.data as Connection[] }, staleTime: 30 * 1000, - enabled: hasProProduct, + enabled: !publicMode && hasProProduct, }) const { data: apiKeys = [], refetch: refetchKeys } = useQuery( @@ -1524,7 +1530,7 @@ export function IntegrationsView() { const data = (await res.json()) as { keys?: ListedApiKey[] } return data.keys ?? [] }, - enabled: !!org?.id, + enabled: !publicMode && !!org?.id, staleTime: 30 * 1000, }, ) @@ -1678,7 +1684,15 @@ export function IntegrationsView() { } } - const availablePluginIds = pluginsData?.plugins ?? Object.keys(PLUGIN_CATALOG) + const redirectToLogin = useCallback(() => { + const loginUrl = new URL("/login", window.location.origin) + loginUrl.searchParams.set("redirect", window.location.href) + window.location.assign(loginUrl.toString()) + }, []) + + const availablePluginIds = publicMode + ? Object.keys(PLUGIN_CATALOG) + : (pluginsData?.plugins ?? Object.keys(PLUGIN_CATALOG)) const enabledPluginIds = new Set( availablePluginIds.filter((id) => PLUGIN_CATALOG[id]), ) @@ -1714,6 +1728,7 @@ export function IntegrationsView() { const isItemConnected = useCallback( (item: Item): boolean => { + if (publicMode) return false if (item.kind === "plugin") { return activePluginById.has(item.pluginId) } @@ -1722,7 +1737,7 @@ export function IntegrationsView() { } return false }, - [activePluginById, connectionsByProvider], + [activePluginById, connectionsByProvider, publicMode], ) const counts = useMemo>( @@ -1780,6 +1795,10 @@ export function IntegrationsView() { ), ctaLabel: "Connect", onCta: () => { + if (publicMode) { + redirectToLogin() + return + } window.open(POKE_RECIPE_URL, "_blank", "noopener,noreferrer") }, }, @@ -1802,6 +1821,10 @@ export function IntegrationsView() { docsUrl: "https://supermemory.ai/docs/supermemory-mcp/introduction", ctaLabel: "Connect", onCta: () => { + if (publicMode) { + redirectToLogin() + return + } void setMcpClient(null) setViewMode("mcp") }, @@ -1831,12 +1854,18 @@ export function IntegrationsView() { /> ), docsUrl: "https://supermemory.ai/docs/integrations/claude-code", - ctaLabel: claudeCodeConnected - ? "Active" - : claudeCodeNeedsPro - ? "Upgrade" - : "Connect", + ctaLabel: publicMode + ? "Connect" + : claudeCodeConnected + ? "Active" + : claudeCodeNeedsPro + ? "Upgrade" + : "Connect", onCta: () => { + if (publicMode) { + redirectToLogin() + return + } if (claudeCodeConnected) return if (claudeCodeNeedsPro) { handleUpgrade() @@ -1855,6 +1884,10 @@ export function IntegrationsView() { backdrop: , ctaLabel: "Connect", onCta: () => { + if (publicMode) { + redirectToLogin() + return + } window.open(CHROME_EXTENSION_URL, "_blank", "noopener,noreferrer") analytics.onboardingChromeExtensionClicked({ source: "integrations" }) }, @@ -1885,6 +1918,19 @@ export function IntegrationsView() { }) const renderRight = (item: Item): ReactNode => { + if (publicMode) { + return ( + { + trackCard(item) + redirectToLogin() + }} + > + Connect + + ) + } + switch (item.kind) { case "plugin": { const activeKey = activePluginById.get(item.pluginId) @@ -2065,6 +2111,8 @@ export function IntegrationsView() { } const renderStatus = (item: Item): ReactNode => { + if (publicMode) return null + switch (item.kind) { case "plugin": { const activeKey = activePluginById.get(item.pluginId) diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts index 4715c5710..2094e5aa3 100644 --- a/apps/web/middleware.ts +++ b/apps/web/middleware.ts @@ -31,6 +31,11 @@ export default async function proxy(request: Request) { return NextResponse.next() } + // Integrations index is public in guest mode; actions still require login. + if (url.pathname === "/" && url.searchParams.get("view") === "integrations") { + return NextResponse.next() + } + if (url.pathname.startsWith("/api/")) { if (!sessionCookie) { console.debug("[MIDDLEWARE] API route without session, returning 401") From 7418abbe8dd0f1790c6a7c3a809a71fbef92f67a Mon Sep 17 00:00:00 2001 From: ishaanxgupta <124028055+ishaanxgupta@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:39:35 +0000 Subject: [PATCH 2/3] memory graph loader (#1072) ## Summary - Replace the memory graph page's top-left package loading indicator with a centered Supermemory loader overlay. - Reuse the same SuperLoader animation in the graph preview card loading state. --- .../web/components/memory-graph/graph-card.tsx | 8 +++++++- .../memory-graph/memory-graph-wrapper.tsx | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/apps/web/components/memory-graph/graph-card.tsx b/apps/web/components/memory-graph/graph-card.tsx index 0269aab8d..de8bed3d2 100644 --- a/apps/web/components/memory-graph/graph-card.tsx +++ b/apps/web/components/memory-graph/graph-card.tsx @@ -4,6 +4,7 @@ import { memo, useMemo } from "react" import { cn } from "@lib/utils" import { dmSansClassName } from "@/lib/fonts" import { Expand } from "lucide-react" +import { SuperLoader } from "@/components/superloader" import { useGraphApi } from "./hooks/use-graph-api" import { useViewMode } from "@/lib/view-mode-context" @@ -157,7 +158,12 @@ export const GraphCard = memo(
{isLoading ? (
-
+
) : documentCount > 0 || memoryCount > 0 ? ( loadMore() : undefined} + isLoading={false} + isLoadingMore={false} + onLoadMore={hasMore && !isLoadingMore ? () => loadMore() : undefined} hasMore={hasMore} error={externalError || apiError} variant={variant} @@ -70,6 +72,16 @@ export function MemoryGraph({ > {children} + {isInitialLoading && ( +
+ +
+ )}
) } From b134716f0e69a8c0e2b0099c734b2d879c7dcdc9 Mon Sep 17 00:00:00 2001 From: MaheshtheDev <38828053+MaheshtheDev@users.noreply.github.com> Date: Tue, 9 Jun 2026 18:16:36 +0000 Subject: [PATCH 3/3] chore(integrations): update Poke recipe URL to supermemory.link/poke (#1074) --- packages/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts index ab721b399..01439d1e6 100644 --- a/packages/lib/constants.ts +++ b/packages/lib/constants.ts @@ -7,7 +7,7 @@ const ADD_MEMORY_SHORTCUT_URL = const RAYCAST_EXTENSION_URL = "https://www.raycast.com/supermemory/supermemory" const CHROME_EXTENSION_URL = "https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnpoaffkcankadgjfc" -const POKE_RECIPE_URL = "https://poke.com/r/5tHPbS8gZvA" +const POKE_RECIPE_URL = "https://supermemory.link/poke" export { BIG_DIMENSIONS_NEW,