[Docs] - Full cleanup (#1105)

<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Removed Features**
  * Dashboard embed functionality has been removed from the platform.
  * Python template documentation and guides have been removed.

* **Documentation**
* Documentation structure has been significantly reorganized and
simplified.
  * Extensive template content and component guides have been removed.
* Apple OAuth integration guide updated with streamlined secret
generation flow.

* **Refactor**
  * Button component styling and variant system updated.
  * Routing configuration updated with simplified path handling.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Madison
2026-01-27 00:38:51 -06:00
committed by GitHub
parent 815b857ef3
commit 9e2cd4d50e
126 changed files with 125 additions and 14782 deletions
-11
View File
@@ -1,11 +0,0 @@
import Footer from '@/components/homepage/homepage-footer';
import { HomeLayout } from '@/components/layouts/home-layout';
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<HomeLayout>
{children}
<Footer />
</HomeLayout>
);
}
-5
View File
@@ -1,5 +0,0 @@
// This page is never reached because of the redirect in next.config.mjs
// Kept as a fallback in case the redirect configuration is removed
export default function HomePage() {
return null;
}
@@ -1,24 +0,0 @@
import { getEmbeddedMDXComponents } from '@/mdx-components';
import { dashboardSource } from 'lib/source';
import { redirect } from 'next/navigation';
export default async function DashboardEmbedPage({
params,
}: {
params: Promise<{ slug?: string[] }>,
}) {
const { slug } = await params;
const page = dashboardSource.getPage(slug ?? []);
if (!page) redirect("/");
const MDX = page.data.body;
return (
<div className="p-6 prose prose-neutral dark:prose-invert max-w-none overflow-x-hidden">
<div className="w-full">
<MDX components={getEmbeddedMDXComponents()} />
</div>
</div>
);
}
-16
View File
@@ -1,16 +0,0 @@
import { EmbeddedLinkInterceptor } from '@/components/embedded-link-interceptor';
// Embedded layout for dashboard docs - no navbar, optimized for iframe
export default function DashboardEmbedLayout({ children }: { children: React.ReactNode }) {
return (
<div className="min-h-screen bg-fd-background">
<EmbeddedLinkInterceptor />
{/* Main content area - no header, no padding, prevent horizontal overflow */}
<main className="h-screen overflow-hidden">
<div className="h-full overflow-y-auto overflow-x-hidden scrollbar-hide">
{children}
</div>
</main>
</div>
);
}
+1 -1
View File
@@ -6,8 +6,8 @@ import { stringCompare } from '@stackframe/stack-shared/dist/utils/strings';
import { AlertTriangle, ChevronDown, Key, X } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { useSidebar } from '../layouts/sidebar-context';
import { Button } from '../mdx/button';
import { useAPIPageContext } from './api-page-wrapper';
import { Button } from './button';
type StackAuthHeaderKey =
| 'X-Stack-Access-Type'
-46
View File
@@ -1,46 +0,0 @@
import { forwardRef } from 'react';
import { cn } from '../../lib/cn';
type ButtonProps = {
variant?: 'default' | 'outline' | 'ghost' | 'secondary',
size?: 'default' | 'sm' | 'lg' | 'icon',
} & React.ButtonHTMLAttributes<HTMLButtonElement>
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = 'default', size = 'default', ...props }, ref) => {
const baseClasses = 'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-fd-primary disabled:pointer-events-none disabled:opacity-50';
const variants = {
default: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/90',
outline: 'border border-fd-border bg-fd-background hover:bg-fd-accent hover:text-fd-accent-foreground',
ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
secondary: 'bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-secondary/80',
};
const sizes = {
default: 'h-9 px-4 py-2 text-sm',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'h-9 w-9',
};
return (
<button
className={cn(
baseClasses,
variants[variant],
sizes[size],
className
)}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = 'Button';
export { Button };
export type { ButtonProps };
@@ -6,7 +6,7 @@ import { useCallback, useEffect, useState } from 'react';
import type { OpenAPIOperation, OpenAPIParameter, OpenAPISchema, OpenAPISpec } from '../../lib/openapi-types';
import { resolveSchema } from '../../lib/openapi-utils';
import { useAPIPageContext } from './api-page-wrapper';
import { Button } from './button';
import { Button } from '../mdx/button';
type EnhancedAPIPageProps = {
document: string,
@@ -3,7 +3,7 @@
import { ArrowRight, Check, Code, Copy, Sparkles, Webhook } from 'lucide-react';
import { useCallback, useEffect, useState } from 'react';
import { codeToHtml } from 'shiki';
import { Button } from './button';
import { Button } from '../mdx/button';
// Types for OpenAPI specification (focused on webhooks)
type OpenAPISchema = {
@@ -24,8 +24,7 @@ const getEmbeddedUrl = (href: string, currentPath: string): string => {
// Already an embedded URL?
if (
cleanPath.startsWith('/docs-embed') ||
cleanPath.startsWith('/api-embed') ||
cleanPath.startsWith('/dashboard-embed')
cleanPath.startsWith('/api-embed')
) {
return withSuffix(cleanPath);
}
@@ -37,9 +36,6 @@ const getEmbeddedUrl = (href: string, currentPath: string): string => {
if (cleanPath === '/api' || cleanPath.startsWith('/api/')) {
return withSuffix(cleanPath.replace(/^\/api(?=\/|$)/, '/api-embed'));
}
if (cleanPath === '/dashboard' || cleanPath.startsWith('/dashboard/')) {
return withSuffix(cleanPath.replace(/^\/dashboard(?=\/|$)/, '/dashboard-embed'));
}
// Relative paths -> resolve against current embedded section (if present)
if (!cleanPath.startsWith('/') && !cleanPath.startsWith('#')) {
@@ -125,7 +121,6 @@ export function EmbeddedLinkInterceptor() {
if (
href.startsWith('/docs/') ||
href.startsWith('/api/') ||
href.startsWith('/dashboard/') ||
(!/^[a-zA-Z][a-zA-Z+\-.]*:/.test(href) && !href.startsWith('#'))
) {
event.preventDefault();
@@ -1,86 +0,0 @@
import Link from 'next/link';
export default function Footer() {
const footerLinks = [
{
title: "Product",
links: [
{ name: "Website", href: "https://stack-auth.com" },
{ name: "Dashboard", href: "https://app.stack-auth.com/projects" }
]
},
{
title: "Community",
links: [
{ name: "GitHub", href: "https://github.com/stack-auth/stack-auth" },
{ name: "Discord", href: "https://discord.stack-auth.com/" }
]
},
{
title: "Company",
links: [
{ name: "Careers", href: "https://jobs.gem.com/stack-auth-com" }
]
}
];
return (
<footer className="border-t border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="max-w-7xl mx-auto px-6 py-12">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8 lg:gap-12">
{/* Logo and Description */}
<div className="md:col-span-1">
<div className="flex items-center mb-4">
<svg
width="32"
height="26"
viewBox="0 0 200 242"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="mr-3"
>
<path d="M103.504 1.81227C101.251 0.68679 98.6002 0.687576 96.3483 1.81439L4.4201 47.8136C1.71103 49.1692 0 51.9387 0 54.968V130.55C0 133.581 1.7123 136.351 4.42292 137.706L96.4204 183.695C98.6725 184.82 101.323 184.82 103.575 183.694L168.422 151.271C173.742 148.611 180 152.479 180 158.426V168.879C180 171.91 178.288 174.68 175.578 176.035L103.577 212.036C101.325 213.162 98.6745 213.162 96.4224 212.036L11.5771 169.623C6.25791 166.964 0 170.832 0 176.779V187.073C0 190.107 1.71689 192.881 4.43309 194.234L96.5051 240.096C98.7529 241.216 101.396 241.215 103.643 240.094L195.571 194.235C198.285 192.881 200 190.109 200 187.076V119.512C200 113.565 193.741 109.697 188.422 112.356L131.578 140.778C126.258 143.438 120 139.57 120 133.623V123.17C120 120.14 121.712 117.37 124.422 116.014L195.578 80.4368C198.288 79.0817 200 76.3116 200 73.2814V54.9713C200 51.9402 198.287 49.1695 195.576 47.8148L103.504 1.81227Z" fill="currentColor"/>
</svg>
<span className="text-xl font-bold">Stack Auth</span>
</div>
<p className="text-sm text-muted-foreground leading-relaxed mb-6">
Complete authentication solution for modern applications. Secure, scalable, and developer-friendly.
</p>
<div className="flex space-x-1">
{['rgb(59, 130, 246)', 'rgb(16, 185, 129)', 'rgb(245, 158, 11)', 'rgb(168, 85, 247)'].map((color, index) => (
<div
key={index}
className="w-3 h-3 rounded-full opacity-60"
style={{ backgroundColor: color }}
/>
))}
</div>
</div>
{/* Footer Links */}
{footerLinks.map((section) => (
<div key={section.title}>
<h3 className="font-semibold text-foreground mb-4 text-sm uppercase tracking-wide">
{section.title}
</h3>
<ul className="space-y-3">
{section.links.map((link) => (
<li key={link.name}>
<Link
href={link.href}
className="text-sm text-muted-foreground hover:text-foreground transition-colors duration-200"
target="_blank"
rel="noopener noreferrer"
>
{link.name}
</Link>
</li>
))}
</ul>
</div>
))}
</div>
</div>
</footer>
);
}
-197
View File
@@ -1,197 +0,0 @@
"use client";
import { Book, Code, Command, Layers, Search, Zap } from "lucide-react";
import React, { useState } from "react";
type DocsSection = {
id: string,
title: string,
description: string,
icon: React.ReactNode,
url: string,
color: string,
}
type DocsIcon3DProps = {
sections?: DocsSection[],
onSectionSelect?: (section: DocsSection) => void,
}
const DEFAULT_SECTIONS: DocsSection[] = [
{
id: "guides",
title: "Guides",
description: "Complete guides and tutorials",
icon: <Book size={24} />,
url: "/docs/overview",
color: "rgb(59, 130, 246)",
},
{
id: "sdk",
title: "SDK",
description: "Software development kits",
icon: <Code size={24} />,
url: "/docs/sdk",
color: "rgb(16, 185, 129)",
},
{
id: "components",
title: "Components",
description: "Reusable UI components",
icon: <Layers size={24} />,
url: "/docs/components",
color: "rgb(245, 101, 101)",
},
{
id: "api",
title: "API Reference",
description: "Complete API documentation",
icon: <Zap size={24} />,
url: "/api/overview",
color: "rgb(168, 85, 247)",
},
];
const DocsIcon3D: React.FC<DocsIcon3DProps> = ({
sections = DEFAULT_SECTIONS,
onSectionSelect,
}) => {
const [hoveredSection, setHoveredSection] = useState<string | null>(null);
const handleSectionClick = (section: DocsSection) => {
if (onSectionSelect) {
onSectionSelect(section);
} else {
window.location.href = section.url;
}
};
// Helper function to convert rgb to rgba
const rgbToRgba = (rgb: string, alpha: number) => {
const match = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (match) {
return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${alpha})`;
}
return rgb;
};
return (
<div className="flex justify-center items-center p-4">
<div
className={`
grid gap-6 max-w-4xl w-full justify-center
${sections.length === 1 ? 'grid-cols-1 max-w-xs' : ''}
${sections.length === 2 ? 'grid-cols-1 md:grid-cols-2 max-w-lg' : ''}
${sections.length === 3 ? 'grid-cols-1 md:grid-cols-3 max-w-2xl' : ''}
${sections.length >= 4 ? 'grid-cols-2 md:grid-cols-4' : ''}
`}
>
{sections.map((section) => (
<div
key={section.id}
className="cursor-pointer group transition-transform hover:scale-105"
onMouseEnter={() => setHoveredSection(section.id)}
onMouseLeave={() => setHoveredSection(null)}
onClick={() => handleSectionClick(section)}
>
<div
className="bg-card border border-border rounded-xl p-4 w-full h-40 flex flex-col items-center justify-center shadow-sm hover:shadow-lg transition-all overflow-hidden"
style={{
borderColor: hoveredSection === section.id ? section.color : rgbToRgba(section.color, 0.4),
}}
>
{/* Icon Container */}
<div className="mb-3 flex-shrink-0">
<div
className="w-10 h-10 rounded-lg flex items-center justify-center transition-all"
style={{
backgroundColor: hoveredSection === section.id ? section.color : rgbToRgba(section.color, 0.2),
color: hoveredSection === section.id ? 'white' : section.color,
transform: hoveredSection === section.id ? 'scale(1.1)' : 'scale(1)',
}}
>
{React.cloneElement(section.icon as React.ReactElement, {
size: 18,
strokeWidth: hoveredSection === section.id ? 2.5 : 2,
})}
</div>
</div>
{/* Title */}
<h3
className="text-sm font-semibold mb-1.5 text-center transition-transform line-clamp-1"
style={{
color: section.color,
transform: hoveredSection === section.id ? 'scale(1.05)' : 'scale(1)',
}}
>
{section.title}
</h3>
{/* Description */}
<p className="text-xs text-center text-muted-foreground leading-snug px-1 opacity-80 line-clamp-2">
{section.description}
</p>
</div>
</div>
))}
</div>
</div>
);
};
export default function DocsSelector() {
const handleSectionSelect = (section: DocsSection) => {
//console.log("Selected section:", section);
// Navigate to the selected section
if (section.url) {
window.location.href = section.url;
}
};
// Simple search button that opens the shared search dialog
const handleSearchOpen = () => {
// Trigger the main search dialog by dispatching the Cmd+K event
const event = new KeyboardEvent('keydown', {
key: 'k',
metaKey: true,
bubbles: true
});
document.dispatchEvent(event);
};
return (
<div className="w-full max-w-4xl mx-auto">
{/* Search Bar - uses shared search dialog */}
<div className="mb-8 flex justify-center">
<div className="w-full max-w-md">
<button
onClick={handleSearchOpen}
className="group flex w-full items-center gap-4 rounded-xl border border-fd-border/60 bg-fd-background/80 px-4 py-4 text-left text-sm text-fd-muted-foreground backdrop-blur-sm hover:border-fd-border hover:bg-fd-background hover:text-fd-foreground hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-fd-primary/20"
>
<Search className="h-5 w-5 flex-shrink-0" />
<div className="flex-1">
<div className="font-medium">Search documentation</div>
<div className="text-xs text-fd-muted-foreground/70">Find guides, API references, and examples</div>
</div>
<div className="flex items-center gap-1">
<kbd className="inline-flex h-7 min-w-[28px] items-center justify-center rounded-md border border-fd-border/60 bg-fd-muted/50 px-2 font-mono text-xs font-medium text-fd-muted-foreground/80 group-hover:border-fd-border group-hover:bg-fd-muted group-hover:text-fd-muted-foreground">
<Command className="h-4 w-4" />
</kbd>
<kbd className="inline-flex h-7 w-7 items-center justify-center rounded-md border border-fd-border/60 bg-fd-muted/50 font-mono text-xs font-medium text-fd-muted-foreground/80 group-hover:border-fd-border group-hover:bg-fd-muted group-hover:text-fd-muted-foreground">
K
</kbd>
</div>
</button>
</div>
</div>
<DocsIcon3D
sections={DEFAULT_SECTIONS}
onSectionSelect={handleSectionSelect}
/>
</div>
);
}
// Export the core component for direct use
export { DocsIcon3D };
@@ -1,77 +0,0 @@
'use client';
import { useI18n } from 'fumadocs-ui/contexts/i18n';
import { useSearchContext } from 'fumadocs-ui/contexts/search';
import { Search } from 'lucide-react';
import type { ComponentProps } from 'react';
import { cn } from '../../lib/cn';
import { type ButtonProps, buttonVariants } from '../ui/button';
type SearchToggleProps = {
hideIfDisabled?: boolean,
} & Omit<ComponentProps<'button'>, 'color'> & ButtonProps
export function SearchToggle({
hideIfDisabled,
size = 'icon',
color = 'ghost',
...props
}: SearchToggleProps) {
const { setOpenSearch, enabled } = useSearchContext();
if (hideIfDisabled && !enabled) return null;
return (
<button
type="button"
className={cn(
buttonVariants({
size,
color,
}),
props.className,
)}
data-search=""
aria-label="Open Search"
onClick={() => {
setOpenSearch(true);
}}
>
<Search className="p-px" />
</button>
);
}
export function LargeSearchToggle({
hideIfDisabled,
...props
}: ComponentProps<'button'> & {
hideIfDisabled?: boolean,
}) {
const { enabled, hotKey, setOpenSearch } = useSearchContext();
const { text } = useI18n();
if (hideIfDisabled && !enabled) return null;
return (
<button
type="button"
data-search-full=""
{...props}
className={cn(
'inline-flex items-center gap-2 rounded-lg border bg-fd-secondary/50 p-1.5 ps-2 text-sm text-fd-muted-foreground transition-colors hover:bg-fd-accent hover:text-fd-accent-foreground',
props.className,
)}
onClick={() => {
setOpenSearch(true);
}}
>
<Search className="size-4" />
{text.search}
<div className="ms-auto inline-flex gap-0.5">
{hotKey.map((k, i) => (
<kbd key={i} className="rounded-md border bg-fd-background px-1.5">
{k.display}
</kbd>
))}
</div>
</button>
);
}
-322
View File
@@ -1,322 +0,0 @@
'use client';
import { UserButton } from '@stackframe/stack';
import { Github, Menu, Sparkles, X } from 'lucide-react';
import Link from 'next/link';
import { useEffect, useState, type ReactNode } from 'react';
import { AIChatDrawer } from '../chat/ai-chat';
import { CustomSearchDialog } from '../layout/custom-search-dialog';
import { SearchInputToggle } from '../layout/custom-search-toggle';
import { ThemeToggle } from '../layout/theme-toggle';
import { SidebarProvider, useSidebar } from './sidebar-context';
// Discord Icon Component
function DiscordIcon({ className }: { className?: string }) {
return (
<svg
className={className}
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418Z"/>
</svg>
);
}
// Stack Auth Logo Component
function StackAuthLogo() {
return (
<Link href="https://stack-auth.com" className="flex items-center gap-2.5 text-fd-foreground hover:text-fd-foreground/80 transition-colors">
<svg
width="30"
height="24"
viewBox="0 0 200 242"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-label="Stack Logo"
className="flex-shrink-0"
>
<path d="M103.504 1.81227C101.251 0.68679 98.6002 0.687576 96.3483 1.81439L4.4201 47.8136C1.71103 49.1692 0 51.9387 0 54.968V130.55C0 133.581 1.7123 136.351 4.42292 137.706L96.4204 183.695C98.6725 184.82 101.323 184.82 103.575 183.694L168.422 151.271C173.742 148.611 180 152.479 180 158.426V168.879C180 171.91 178.288 174.68 175.578 176.035L103.577 212.036C101.325 213.162 98.6745 213.162 96.4224 212.036L11.5771 169.623C6.25791 166.964 0 170.832 0 176.779V187.073C0 190.107 1.71689 192.881 4.43309 194.234L96.5051 240.096C98.7529 241.216 101.396 241.215 103.643 240.094L195.571 194.235C198.285 192.881 200 190.109 200 187.076V119.512C200 113.565 193.741 109.697 188.422 112.356L131.578 140.778C126.258 143.438 120 139.57 120 133.623V123.17C120 120.14 121.712 117.37 124.422 116.014L195.578 80.4368C198.288 79.0817 200 76.3116 200 73.2814V54.9713C200 51.9402 198.287 49.1695 195.576 47.8148L103.504 1.81227Z" fill="currentColor"/>
</svg>
<span className="font-medium text-[15px]">Stack Auth</span>
</Link>
);
}
// AI Chat Toggle Button for Home Layout
function HomeAIChatToggleButton({ compact = false }: { compact?: boolean }) {
const sidebarContext = useSidebar();
const { isChatOpen, toggleChat } = sidebarContext || {
isChatOpen: false,
toggleChat: () => {},
};
if (compact) {
return (
<button
onClick={toggleChat}
className="flex items-center justify-center transition-all duration-500 ease-out w-8 h-8 rounded-lg text-sm font-medium relative overflow-hidden text-white chat-gradient-active hover:scale-105 hover:brightness-110 hover:shadow-lg"
title={isChatOpen ? 'Close AI chat' : 'Open AI chat'}
>
<Sparkles className="h-4 w-4 relative z-10" />
</button>
);
}
return (
<button
onClick={toggleChat}
className="flex items-center gap-2 transition-all duration-500 ease-out px-2 py-1 rounded-lg text-xs font-medium relative overflow-hidden text-white chat-gradient-active hover:scale-105 hover:brightness-110 hover:shadow-lg"
title={isChatOpen ? 'Close AI chat' : 'Open AI chat'}
>
<Sparkles className="h-3 w-3 relative z-10" />
<span className="font-medium relative z-10">AI Chat</span>
</button>
);
}
// Home Navbar Component
function HomeNavbar() {
const [searchOpen, setSearchOpen] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
const sidebarContext = useSidebar();
const { isChatOpen, isChatExpanded } = sidebarContext || {
isChatOpen: false,
isChatExpanded: false,
};
// Scroll detection
useEffect(() => {
const handleScroll = () => {
const scrollY = window.scrollY;
setIsScrolled(scrollY > 50);
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// Close mobile menu when scrolling
useEffect(() => {
if (isScrolled && mobileMenuOpen) {
setMobileMenuOpen(false);
}
}, [isScrolled, mobileMenuOpen]);
return (
<>
{/* Full Navbar */}
<header className={`sticky top-0 z-50 w-full border-b border-fd-border bg-fd-background/95 backdrop-blur supports-[backdrop-filter]:bg-fd-background/60 transition-all duration-300 ${isScrolled ? 'opacity-0 pointer-events-none' : 'opacity-100'} ${(isChatOpen || isChatExpanded) ? 'fixed left-0 right-0 z-[80]' : ''}`}>
<div className="flex h-14 items-center justify-between px-4 md:px-6 container max-w-screen-2xl">
{/* Left - Logo + Social Links */}
<div className="flex items-center gap-4">
<StackAuthLogo />
{/* Desktop Social Links */}
<div className="hidden md:flex items-center gap-1">
<Link
href="https://github.com/stack-auth/stack"
target="_blank"
rel="noopener noreferrer"
className="inline-flex h-9 w-9 items-center justify-center rounded-lg text-fd-muted-foreground transition-colors hover:bg-fd-muted hover:text-fd-foreground"
title="GitHub"
>
<Github className="h-4 w-4" />
</Link>
<Link
href="https://discord.stack-auth.com"
target="_blank"
rel="noopener noreferrer"
className="inline-flex h-9 w-9 items-center justify-center rounded-lg text-fd-muted-foreground transition-colors hover:bg-fd-muted hover:text-fd-foreground"
title="Discord"
>
<DiscordIcon className="h-4 w-4" />
</Link>
</div>
</div>
{/* Right - Search + Actions */}
<div className="flex items-center gap-2">
{/* Desktop Search */}
<div className="hidden md:block w-64">
<SearchInputToggle
onOpen={() => setSearchOpen(true)}
className="w-full"
/>
</div>
{/* AI Chat Toggle */}
<HomeAIChatToggleButton />
{/* Theme Toggle */}
<ThemeToggle className="p-0" />
{/* User Sign in */}
<UserButton />
{/* Mobile Search */}
<div className="md:hidden">
<SearchInputToggle
onOpen={() => setSearchOpen(true)}
className="w-9"
/>
</div>
{/* Mobile Menu Toggle */}
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="md:hidden inline-flex h-9 w-9 items-center justify-center rounded-lg text-fd-muted-foreground transition-colors hover:bg-fd-muted hover:text-fd-foreground"
aria-label="Toggle menu"
>
{mobileMenuOpen ? <X className="h-4 w-4" /> : <Menu className="h-4 w-4" />}
</button>
</div>
</div>
{/* Mobile Menu */}
{mobileMenuOpen && (
<div className="md:hidden border-t border-fd-border bg-fd-background">
<div className="container px-4 py-4 space-y-3">
<Link
href="https://github.com/stack-auth/stack"
target="_blank"
rel="noopener noreferrer"
onClick={() => setMobileMenuOpen(false)}
className="flex items-center gap-3 px-3 py-2 rounded-lg text-fd-muted-foreground hover:bg-fd-muted hover:text-fd-foreground transition-colors"
>
<Github className="h-4 w-4" />
<span>GitHub</span>
</Link>
<Link
href="https://discord.stack-auth.com"
target="_blank"
rel="noopener noreferrer"
onClick={() => setMobileMenuOpen(false)}
className="flex items-center gap-3 px-3 py-2 rounded-lg text-fd-muted-foreground hover:bg-fd-muted hover:text-fd-foreground transition-colors"
>
<DiscordIcon className="h-4 w-4" />
<span>Discord</span>
</Link>
</div>
</div>
)}
</header>
{/* Compact Pill Navbar */}
<div className={`fixed top-4 left-1/2 -translate-x-1/2 transition-all duration-300 ${isScrolled ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-4 pointer-events-none'} ${isChatExpanded ? 'z-[80]' : 'z-50'}`}>
<div className="flex items-center gap-3 px-4 py-2 bg-fd-background/95 backdrop-blur border border-fd-border rounded-full shadow-lg supports-[backdrop-filter]:bg-fd-background/80">
{/* Left Side - Social Links and Search */}
<div className="flex items-center gap-1">
<Link
href="https://github.com/stack-auth/stack"
target="_blank"
rel="noopener noreferrer"
className="inline-flex h-8 w-8 items-center justify-center rounded-full text-fd-muted-foreground transition-colors hover:bg-fd-muted hover:text-fd-foreground"
title="GitHub"
>
<Github className="h-4 w-4" />
</Link>
<Link
href="https://discord.stack-auth.com"
target="_blank"
rel="noopener noreferrer"
className="inline-flex h-8 w-8 items-center justify-center rounded-full text-fd-muted-foreground transition-colors hover:bg-fd-muted hover:text-fd-foreground"
title="Discord"
>
<DiscordIcon className="h-4 w-4" />
</Link>
{/* Compact Search */}
<button
onClick={() => setSearchOpen(true)}
className="inline-flex h-8 w-8 items-center justify-center rounded-full text-fd-muted-foreground transition-colors hover:bg-fd-muted hover:text-fd-foreground"
title="Search (⌘K)"
>
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</button>
</div>
{/* Center - Much Larger Logo */}
<Link href="https://stack-auth.com" className="flex items-center text-fd-foreground hover:text-fd-foreground/80 transition-colors">
<svg
width="40"
height="32"
viewBox="0 0 200 242"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-label="Stack Logo"
className="flex-shrink-0"
>
<path d="M103.504 1.81227C101.251 0.68679 98.6002 0.687576 96.3483 1.81439L4.4201 47.8136C1.71103 49.1692 0 51.9387 0 54.968V130.55C0 133.581 1.7123 136.351 4.42292 137.706L96.4204 183.695C98.6725 184.82 101.323 184.82 103.575 183.694L168.422 151.271C173.742 148.611 180 152.479 180 158.426V168.879C180 171.91 178.288 174.68 175.578 176.035L103.577 212.036C101.325 213.162 98.6745 213.162 96.4224 212.036L11.5771 169.623C6.25791 166.964 0 170.832 0 176.779V187.073C0 190.107 1.71689 192.881 4.43309 194.234L96.5051 240.096C98.7529 241.216 101.396 241.215 103.643 240.094L195.571 194.235C198.285 192.881 200 190.109 200 187.076V119.512C200 113.565 193.741 109.697 188.422 112.356L131.578 140.778C126.258 143.438 120 139.57 120 133.623V123.17C120 120.14 121.712 117.37 124.422 116.014L195.578 80.4368C198.288 79.0817 200 76.3116 200 73.2814V54.9713C200 51.9402 198.287 49.1695 195.576 47.8148L103.504 1.81227Z" fill="currentColor"/>
</svg>
</Link>
{/* Right Side - Actions */}
<div className="flex items-center gap-1">
{/* Compact Theme Toggle */}
<ThemeToggle compact />
{/* Compact AI Chat Toggle */}
<HomeAIChatToggleButton compact />
{/* Compact User Button */}
<UserButton />
</div>
</div>
</div>
{/* Search Dialog */}
<CustomSearchDialog
open={searchOpen}
onOpenChange={setSearchOpen}
/>
</>
);
}
// Main Home Layout Component
export function HomeLayout({ children }: { children: ReactNode }) {
// Add home-page class to body to exclude from chat content shifting
useEffect(() => {
document.body.classList.add('home-page');
return () => {
document.body.classList.remove('home-page');
};
}, []);
// Add scroll detection for homepage
useEffect(() => {
const handleScroll = () => {
const scrollY = window.scrollY;
const isScrolled = scrollY > 50;
if (isScrolled) {
document.body.classList.add('scrolled');
} else {
document.body.classList.remove('scrolled');
}
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
document.body.classList.remove('scrolled');
};
}, []);
return (
<SidebarProvider>
<div className="relative flex min-h-screen flex-col bg-fd-background">
<HomeNavbar />
<main className="flex-1">
{children}
</main>
<AIChatDrawer />
</div>
</SidebarProvider>
);
}
@@ -1,288 +0,0 @@
'use client';
import {
Background,
Edge,
Handle,
MarkerType,
Node,
NodeTypes,
Position,
ReactFlow,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
// Actor node component - vertical lifelines with alternating sections
const ActorNode = ({ data }: { data: { label: string, sections: { highlighted: boolean, id: string }[] } }) => {
return (
<div className="flex flex-col items-center">
{/* Actor header */}
<div className="px-4 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white text-center font-medium text-sm mb-2">
{data.label}
</div>
{/* Vertical lifeline with sections */}
<div className="flex flex-col items-center">
{data.sections.map((section) => (
<div key={section.id} className="relative">
{section.highlighted ? (
// Highlighted section (box)
<div className="w-12 h-16 bg-gray-800 border border-gray-600 rounded flex items-center justify-center">
<Handle
type="target"
position={Position.Left}
id={`${section.id}-left`}
style={{ left: -8, background: 'transparent', border: 'none', width: 16, height: 16 }}
/>
<Handle
type="source"
position={Position.Right}
id={`${section.id}-right`}
style={{ right: -8, background: 'transparent', border: 'none', width: 16, height: 16 }}
/>
</div>
) : (
// Regular line section
<div className="w-0.5 h-16 bg-gray-400 flex items-center justify-center relative">
<Handle
type="target"
position={Position.Left}
id={`${section.id}-left`}
style={{ left: -8, background: 'transparent', border: 'none', width: 16, height: 16 }}
/>
<Handle
type="source"
position={Position.Right}
id={`${section.id}-right`}
style={{ right: -8, background: 'transparent', border: 'none', width: 16, height: 16 }}
/>
</div>
)}
</div>
))}
</div>
</div>
);
};
// Action node component - text between actors
const ActionNode = ({ data }: { data: { label: string, dashed?: boolean } }) => {
return (
<div className={`px-3 py-1 bg-gray-800 border border-gray-600 rounded text-white text-sm font-medium whitespace-nowrap ${data.dashed ? 'border-dashed' : ''}`}>
{data.label}
<Handle type="target" position={Position.Left} style={{ background: 'transparent', border: 'none' }} />
<Handle type="source" position={Position.Right} style={{ background: 'transparent', border: 'none' }} />
</div>
);
};
const nodeTypes: NodeTypes = {
actor: ActorNode,
action: ActionNode,
};
const initialNodes: Node[] = [
// Actor nodes with their lifeline sections
{
id: 'user-actor',
type: 'actor',
position: { x: 50, y: 50 },
data: {
label: 'User/Client',
sections: [
{ highlighted: true, id: 'user-1' },
{ highlighted: false, id: 'user-2' },
{ highlighted: false, id: 'user-3' },
{ highlighted: false, id: 'user-4' },
{ highlighted: true, id: 'user-5' },
]
},
draggable: false,
},
{
id: 'server-actor',
type: 'actor',
position: { x: 300, y: 50 },
data: {
label: 'Your Application Server',
sections: [
{ highlighted: true, id: 'server-1' },
{ highlighted: true, id: 'server-2' },
{ highlighted: true, id: 'server-3' },
{ highlighted: true, id: 'server-4' },
{ highlighted: true, id: 'server-5' },
]
},
draggable: false,
},
{
id: 'auth-actor',
type: 'actor',
position: { x: 600, y: 50 },
data: {
label: 'Stack Auth Service',
sections: [
{ highlighted: false, id: 'auth-1' },
{ highlighted: true, id: 'auth-2' },
{ highlighted: true, id: 'auth-3' },
{ highlighted: false, id: 'auth-4' },
{ highlighted: false, id: 'auth-5' },
]
},
draggable: false,
},
// Action nodes
{
id: 'action-1',
type: 'action',
position: { x: 175, y: 120 },
data: { label: 'API request with API key' },
draggable: false,
},
{
id: 'action-2',
type: 'action',
position: { x: 450, y: 180 },
data: { label: 'Validate API key' },
draggable: false,
},
{
id: 'action-3',
type: 'action',
position: { x: 450, y: 240 },
data: { label: 'Return authenticated User object', dashed: true },
draggable: false,
},
{
id: 'action-4',
type: 'action',
position: { x: 300, y: 300 },
data: { label: 'Process request' },
draggable: false,
},
{
id: 'action-5',
type: 'action',
position: { x: 175, y: 360 },
data: { label: 'Response with data', dashed: true },
draggable: false,
},
];
const initialEdges: Edge[] = [
// Step 1: API request
{
id: 'edge-1',
source: 'user-actor',
sourceHandle: 'user-1-right',
target: 'action-1',
style: { stroke: '#ffffff', strokeWidth: 2 },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
{
id: 'edge-1b',
source: 'action-1',
target: 'server-actor',
targetHandle: 'server-1-left',
style: { stroke: '#ffffff', strokeWidth: 2 },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
// Step 2: Validate API key
{
id: 'edge-2',
source: 'server-actor',
sourceHandle: 'server-2-right',
target: 'action-2',
style: { stroke: '#ffffff', strokeWidth: 2 },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
{
id: 'edge-2b',
source: 'action-2',
target: 'auth-actor',
targetHandle: 'auth-2-left',
style: { stroke: '#ffffff', strokeWidth: 2 },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
// Step 3: Return user object (dashed)
{
id: 'edge-3',
source: 'auth-actor',
sourceHandle: 'auth-3-left',
target: 'action-3',
style: { stroke: '#ffffff', strokeWidth: 2, strokeDasharray: '5,5' },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
{
id: 'edge-3b',
source: 'action-3',
target: 'server-actor',
targetHandle: 'server-3-right',
style: { stroke: '#ffffff', strokeWidth: 2, strokeDasharray: '5,5' },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
// Step 4: Process request (self-loop)
{
id: 'edge-4',
source: 'server-actor',
sourceHandle: 'server-4-right',
target: 'action-4',
style: { stroke: '#ffffff', strokeWidth: 2 },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
{
id: 'edge-4b',
source: 'action-4',
target: 'server-actor',
targetHandle: 'server-4-left',
style: { stroke: '#ffffff', strokeWidth: 2 },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
// Step 5: Response with data (dashed)
{
id: 'edge-5',
source: 'server-actor',
sourceHandle: 'server-5-left',
target: 'action-5',
style: { stroke: '#ffffff', strokeWidth: 2, strokeDasharray: '5,5' },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
{
id: 'edge-5b',
source: 'action-5',
target: 'user-actor',
targetHandle: 'user-5-right',
style: { stroke: '#ffffff', strokeWidth: 2, strokeDasharray: '5,5' },
markerEnd: { type: MarkerType.ArrowClosed, color: '#ffffff' },
},
];
export default function ApiSequenceDiagram() {
return (
<div className="w-full h-[500px] bg-gray-900 rounded-lg border border-gray-700">
<ReactFlow
nodes={initialNodes}
edges={initialEdges}
nodeTypes={nodeTypes}
fitView
fitViewOptions={{ padding: 50 }}
nodesDraggable={false}
nodesConnectable={false}
elementsSelectable={false}
panOnDrag={false}
zoomOnScroll={false}
zoomOnPinch={false}
zoomOnDoubleClick={false}
proOptions={{ hideAttribution: true }}
className="bg-gray-900"
>
<Background color="#374151" gap={20} />
</ReactFlow>
</div>
);
}
+17 -6
View File
@@ -3,20 +3,26 @@ import * as React from "react";
import { cn } from "../../lib/cn";
import { buttonVariants } from "../ui/button";
type ColorVariant = 'primary' | 'default' | 'secondary' | 'outline' | 'ghost';
type SizeVariant = 'default' | 'sm' | 'lg' | 'icon' | 'icon-sm';
type BaseButtonProps = {
color?: 'primary' | 'secondary' | 'outline' | 'ghost',
size?: 'sm' | 'icon' | 'icon-sm',
/** @deprecated Use `variant` instead */
color?: ColorVariant,
/** Alias for `color` - preferred prop name */
variant?: ColorVariant,
size?: SizeVariant,
icon?: React.ReactNode,
children: React.ReactNode,
}
type ButtonAsButton = BaseButtonProps &
React.ButtonHTMLAttributes<HTMLButtonElement> & {
Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'color'> & {
href?: never,
};
type ButtonAsLink = BaseButtonProps &
React.AnchorHTMLAttributes<HTMLAnchorElement> & {
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'color'> & {
href: string,
};
@@ -25,7 +31,10 @@ type ButtonProps = ButtonAsButton | ButtonAsLink;
export const Button = React.forwardRef<
HTMLButtonElement | HTMLAnchorElement,
ButtonProps
>(({ className, color = 'secondary', size = 'sm', icon, href, children, ...props }, ref) => {
>(({ className, color, variant, size = 'sm', icon, href, children, ...props }, ref) => {
// Support both `variant` and `color` props (variant takes precedence)
const resolvedColor = variant ?? color ?? 'secondary';
const buttonContent = (
<>
{icon && <span className="inline-flex items-center justify-center w-3.5 h-3.5">{icon}</span>}
@@ -35,7 +44,7 @@ export const Button = React.forwardRef<
const buttonClasses = cn(
buttonVariants({
color,
color: resolvedColor,
size,
className: 'gap-2 no-underline hover:no-underline'
}),
@@ -68,3 +77,5 @@ export const Button = React.forwardRef<
});
Button.displayName = "Button";
export type { ButtonProps };
+2 -5
View File
@@ -23,7 +23,7 @@ const getEmbeddedUrl = (href: string, currentPath?: string): string => {
// Handle absolute paths
if (href.startsWith('/')) {
// Already embedded - leave as is
if (href.startsWith('/docs-embed/') || href.startsWith('/api-embed/') || href.startsWith('/dashboard-embed/')) {
if (href.startsWith('/docs-embed/') || href.startsWith('/api-embed/')) {
return href;
}
@@ -34,9 +34,6 @@ const getEmbeddedUrl = (href: string, currentPath?: string): string => {
if (href.startsWith('/api/')) {
return href.replace('/api/', '/api-embed/');
}
if (href.startsWith('/dashboard/')) {
return href.replace('/dashboard/', '/dashboard-embed/');
}
// Other absolute paths - leave as is
return href;
@@ -44,7 +41,7 @@ const getEmbeddedUrl = (href: string, currentPath?: string): string => {
// Handle relative links (like ./setup.mdx or users.mdx)
// These need to be resolved relative to the current embedded path
if (currentPath && (currentPath.startsWith('/docs-embed/') || currentPath.startsWith('/api-embed/') || currentPath.startsWith('/dashboard-embed/'))) {
if (currentPath && (currentPath.startsWith('/docs-embed/') || currentPath.startsWith('/api-embed/'))) {
// Remove .mdx extension if present
const cleanHref = href.replace(/\.mdx?$/, '');
@@ -1,66 +0,0 @@
// Centralized platform and framework configuration
export type PlatformConfig = {
[platformName: string]: {
[frameworkName: string]: {
defaultFilename?: string,
language: string,
},
},
}
export const PLATFORM_CONFIG: PlatformConfig = {
"Python": {
"Django": {
defaultFilename: "views.py",
language: "python"
},
"FastAPI": {
defaultFilename: "main.py",
language: "python"
},
"Flask": {
defaultFilename: "app.py",
language: "python"
}
},
"JavaScript": {
"Next.js": {
defaultFilename: "app/api/route.ts",
language: "typescript"
},
"Express": {
defaultFilename: "server.js",
language: "javascript"
},
"React": {
defaultFilename: "components/LoginForm.tsx",
language: "typescript"
},
"Node.js": {
defaultFilename: "index.js",
language: "javascript"
}
}
};
// Helper function to get available platforms
export function getAvailablePlatforms(): string[] {
return Object.keys(PLATFORM_CONFIG);
}
// Helper function to get frameworks for a platform
export function getFrameworksForPlatform(platform: string): string[] {
return Object.keys(PLATFORM_CONFIG[platform]);
}
// Helper function to get config for a platform/framework combination
export function getPlatformFrameworkConfig(platform: string, framework: string) {
return PLATFORM_CONFIG[platform][framework];
}
// Default framework preferences (can be overridden)
export const DEFAULT_FRAMEWORK_PREFERENCES: { [platform: string]: string } = {
"Python": "Django",
"JavaScript": "Next.js"
};
@@ -189,14 +189,3 @@ export function SignInPasswordFirstTab() {
</StackContainer>
);
}
export function SignInExtraInfo() {
return (
<StackContainer color="blue">
<SignIn
extraInfo={<>By signing in, you agree to our <a href="#" className="text-fd-primary hover:underline">Terms of Service</a></>}
mockProject={mockProject}
/>
</StackContainer>
);
}
+12 -4
View File
@@ -1,23 +1,31 @@
import { cva, type VariantProps } from 'class-variance-authority';
export const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50',
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors duration-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-fd-primary disabled:pointer-events-none disabled:opacity-50',
{
variants: {
color: {
primary:
'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground',
default:
'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/90',
outline: 'border border-fd-border bg-fd-background hover:bg-fd-accent hover:text-fd-accent-foreground',
ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
secondary:
'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground',
},
size: {
sm: 'gap-1 px-2 py-1.5 text-xs',
icon: 'p-1.5 [&_svg]:size-5',
default: 'h-9 px-4 py-2',
sm: 'h-8 gap-1 px-3 py-1.5 text-xs rounded-md',
lg: 'h-10 px-8 rounded-md',
icon: 'h-9 w-9 p-1.5 [&_svg]:size-5',
'icon-sm': 'p-1.5 [&_svg]:size-4.5',
},
},
defaultVariants: {
color: 'primary',
size: 'default',
},
},
);
+3 -7
View File
@@ -11,7 +11,6 @@ import { WebhooksAPIPage } from './components/api/webhooks-api-page';
import AppleSecretGenerator from './components/apple-secret-generator';
import { Card, CardGroup, Info, QuickLink, QuickLinks } from './components/mdx';
import ApiSequenceDiagram from './components/mdx/api-sequence-diagram';
import { AuthCard } from './components/mdx/auth-card';
import { DynamicCodeblock } from './components/mdx/dynamic-code-block';
import { EmbeddedLink } from './components/mdx/embedded-link';
@@ -29,21 +28,21 @@ import { Accordion, AccordionGroup, ClickableTableOfContents, Icon, Markdown, Pa
import { PropTable } from './components/prop-table';
import { ImageZoom } from 'fumadocs-ui/components/image-zoom';
import DocsSelector from './components/homepage/iconHover';
import { AppCard, AppGrid } from './components/mdx/app-card';
import { SignInDemo, SignInExtraInfo, SignInPasswordFirstTab, SignInStackAuth } from './components/stack-auth/sign-in';
import { SignInDemo, SignInPasswordFirstTab, SignInStackAuth } from './components/stack-auth/sign-in';
import { AccountSettingsStackAuth } from './components/stack-auth/stack-account-settings';
import { TeamSwitcherDemo } from './components/stack-auth/stack-team-switcher';
import { StackUserButton } from './components/stack-auth/stack-user-button';
import { UserButtonDemo } from './components/stack-auth/stack-user-button-demo';
import { Step, Steps } from './components/steps';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './components/ui/tabs';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultMdxComponents,
...components,
...CodeBlock,
//SignIn
// Components
Card,
CardGroup,
QuickLink,
@@ -54,7 +53,6 @@ export function getMDXComponents(components?: MDXComponents): MDXComponents {
SignInDemo,
AuthCard,
AccountSettingsStackAuth,
SignInExtraInfo,
StackUserButton,
UserButtonDemo,
TeamSwitcherDemo,
@@ -69,7 +67,6 @@ export function getMDXComponents(components?: MDXComponents): MDXComponents {
WebhooksAPIPage,
TypeTable,
PropTable,
ApiSequenceDiagram,
// SDK Documentation Components
Markdown,
ParamField,
@@ -99,7 +96,6 @@ export function getMDXComponents(components?: MDXComponents): MDXComponents {
// App Components
AppCard,
AppGrid,
DocsSelector,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
img: (props) => <ImageZoom {...(props as any)} />,
} as MDXComponents;