Compare commits

..

12 Commits

29 changed files with 4898 additions and 4 deletions

View File

@ -33,11 +33,14 @@
"DATABASE_URI": "postgresql://banatie_user:banatie_secure_password@localhost:5460/banatie_db" "DATABASE_URI": "postgresql://banatie_user:banatie_secure_password@localhost:5460/banatie_db"
} }
}, },
"mastra": { "perplexity": {
"type": "stdio", "type": "stdio",
"command": "npx", "command": "npx",
"args": ["@mastra/mcp-docs-server@latest"], "args": ["-y", "perplexity-mcp"],
"env": {} "env": {
"PERPLEXITY_API_KEY": "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",
"PERPLEXITY_TIMEOUT_MS": "600000"
}
}, },
"browsermcp": { "browsermcp": {
"type": "stdio", "type": "stdio",

View File

@ -6,7 +6,8 @@
"dev": "next dev -p 3010", "dev": "next dev -p 3010",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"deploy": "cp -r out/* /var/www/banatie.app/" "deploy": "cp -r out/* /var/www/banatie.app/",
"typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"react": "19.1.0", "react": "19.1.0",

View File

@ -0,0 +1,226 @@
'use client';
/**
* API Reference: Text to Image
*
* Refactored to use DocPage component for consistent layout
* Layout handles SubsectionNav and Left Sidebar
* DocPage handles Breadcrumb, Article structure, Next Steps, and TOC
* This page provides only the content (Hero + sections)
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import { InteractiveAPIWidget } from '@/components/docs/blocks/InteractiveAPIWidget';
import {
Hero,
SectionHeader,
InlineCode,
EndpointCard,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'overview', text: 'Overview', level: 2 },
{ id: 'endpoint', text: 'Endpoint', level: 2 },
{ id: 'parameters', text: 'Parameters', level: 2 },
{ id: 'response', text: 'Response', level: 2 },
{ id: 'error-codes', text: 'Error Codes', level: 2 },
{ id: 'interactive', text: 'Try It Live', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
const parameters = [
{ name: 'prompt', type: 'string', required: true, description: 'Text description of the image to generate' },
{ name: 'filename', type: 'string', required: false, description: 'Output filename (without extension)' },
{ name: 'aspectRatio', type: 'string', required: false, description: 'Image aspect ratio: "1:1", "16:9", "9:16", "4:3"' },
{ name: 'autoEnhance', type: 'boolean', required: false, description: 'Enable AI prompt enhancement for better results' },
];
export default function TextToImageAPIPage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'API Reference', href: '/docs/api' },
{ label: 'Text to Image' },
]}
tocItems={tocItems}
nextSteps={{
links: [
{
href: '/docs/api/upload',
title: 'Upload API',
description: 'Learn how to upload reference images for image-to-image generation.',
accent: 'primary',
},
{
href: '/docs/guides/error-handling',
title: 'Error Handling',
description: 'Best practices for handling errors and retries in production.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Text to Image"
subtitle="Generate high-quality images from text prompts using AI-powered models."
/>
{/* Overview */}
<section id="overview" className="mb-12">
<SectionHeader level={2} id="overview">
Overview
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
The Text to Image endpoint allows you to generate images from natural language descriptions.
Powered by Google Gemini 2.5 Flash and Imagen 4.0, it produces photorealistic images
optimized for your specified requirements.
</p>
<TipBox variant="compact" type="info">
<strong>Tip:</strong> Enable <InlineCode>autoEnhance</InlineCode>
to let AI improve your prompts for better image quality.
</TipBox>
</section>
{/* Endpoint */}
<section id="endpoint" className="mb-12">
<SectionHeader level={2} id="endpoint">
Endpoint
</SectionHeader>
<EndpointCard
method="POST"
endpoint="/api/text-to-image"
baseUrl="https://api.banatie.com"
/>
</section>
{/* Parameters */}
<section id="parameters" className="mb-12">
<SectionHeader level={2} id="parameters" className="mb-6">
Parameters
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
All parameters should be sent in the request body as JSON.
</p>
<Table
headers={['Parameter', 'Type', 'Required', 'Description']}
rows={parameters.map((param) => [
<InlineCode key="name">{param.name}</InlineCode>,
<span key="type" className="text-cyan-400">{param.type}</span>,
<span key="required" className={param.required ? 'text-green-400' : 'text-gray-500'}>
{param.required ? 'Yes' : 'No'}
</span>,
param.description,
])}
/>
<div className="mt-6">
<TipBox variant="compact" type="info">
<strong>Default Values:</strong> If not specified, <InlineCode>filename</InlineCode> is
auto-generated, <InlineCode>aspectRatio</InlineCode> defaults
to "1:1", and <InlineCode>autoEnhance</InlineCode> is false.
</TipBox>
</div>
</section>
{/* Response */}
<section id="response" className="mb-12">
<SectionHeader level={2} id="response" className="mb-4">
Response
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
On success, the API returns a JSON object containing the generated image URL and metadata.
</p>
<CodeBlock
code={`{
"success": true,
"data": {
"url": "https://cdn.banatie.com/org/project/generated/2025-01/image.png",
"filepath": "org/project/generated/2025-01/image.png",
"width": 1024,
"height": 1024,
"promptEnhancement": {
"enhancedPrompt": "A highly detailed, photorealistic...",
"wasEnhanced": true
}
},
"metadata": {
"generationTime": 3.42,
"model": "imagen-4.0"
}
}`}
language="json"
filename="Success Response"
/>
</section>
{/* Error Codes */}
<section id="error-codes" className="mb-12">
<SectionHeader level={2} id="error-codes" className="mb-6">
Error Codes
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
The API uses standard HTTP status codes and returns descriptive error messages.
</p>
<Table
headers={['Status Code', 'Error Type', 'Description']}
rows={[
[
<InlineCode key="code" color="error">400</InlineCode>,
'Bad Request',
'Missing or invalid parameters in the request body',
],
[
<InlineCode key="code" color="error">401</InlineCode>,
'Unauthorized',
'Missing or invalid API key in X-API-Key header',
],
[
<InlineCode key="code" color="error">429</InlineCode>,
'Rate Limit',
'Too many requests. Check rate limit headers for retry timing',
],
[
<InlineCode key="code" color="error">500</InlineCode>,
'Server Error',
'Internal server error. Contact support if persists',
],
]}
/>
<div className="mt-6">
<TipBox variant="compact" type="warning">
<strong>Rate Limits:</strong> Project API keys are limited to 100 requests per hour.
Upgrade your plan for higher limits.
</TipBox>
</div>
</section>
{/* Interactive Widget */}
<section id="interactive" className="mb-12">
<SectionHeader level={2} id="interactive" className="mb-4">
Try It Live
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Test the API directly from this page. Enter your API key and customize the parameters below.
</p>
<InteractiveAPIWidget
endpoint="/api/text-to-image"
method="POST"
description="Generate an image from a text prompt"
parameters={parameters}
/>
</section>
</DocPage>
);
}

View File

@ -0,0 +1,313 @@
'use client';
/**
* Authentication Guide
*
* Refactored to use DocPage component for consistent layout
* Layout handles SubsectionNav and Left Sidebar
* DocPage handles Breadcrumb, Article structure, Next Steps, and TOC
* This page provides only the content (Hero + sections)
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import {
Hero,
SectionHeader,
InlineCode,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'overview', text: 'Overview', level: 2 },
{ id: 'api-keys', text: 'API Keys', level: 2 },
{ id: 'key-types', text: 'Key Types', level: 3 },
{ id: 'creating-keys', text: 'Creating Keys', level: 3 },
{ id: 'using-keys', text: 'Using API Keys', level: 2 },
{ id: 'rate-limits', text: 'Rate Limits', level: 2 },
{ id: 'security', text: 'Security Best Practices', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
export default function AuthenticationGuidePage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'Guides', href: '/docs/guides' },
{ label: 'Authentication' },
]}
tocItems={tocItems}
nextSteps={{
links: [
{
href: '/docs/api/text-to-image',
title: 'Start Generating Images',
description: 'Explore the Text to Image API and start building your integration.',
accent: 'primary',
},
{
href: '/docs/guides/error-handling',
title: 'Error Handling Guide',
description: 'Learn how to handle authentication errors and implement retry logic.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Authentication"
subtitle="Learn how to authenticate with the Banatie API using API keys, manage rate limits, and implement security best practices."
/>
{/* Overview */}
<section id="overview" className="mb-12">
<SectionHeader level={2} id="overview" className="mb-4">
Overview
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Banatie uses API keys to authenticate requests. All API endpoints require authentication
via the <InlineCode>X-API-Key</InlineCode> header.
API keys are tied to organizations and projects, providing fine-grained access control.
</p>
<TipBox variant="compact" type="info">
<strong>Quick Start:</strong> New to API authentication? Check out our{' '}
<a href="/docs" className="text-purple-400 hover:underline">
Getting Started guide
</a>{' '}
for a step-by-step walkthrough.
</TipBox>
</section>
{/* API Keys */}
<section id="api-keys" className="mb-12">
<SectionHeader level={2} id="api-keys" className="mb-6">
API Keys
</SectionHeader>
<div id="key-types" className="mb-8">
<SectionHeader level={3} id="key-types" className="mb-4">
Key Types
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Banatie supports two types of API keys, each with different permissions and use cases:
</p>
<Table
headers={['Key Type', 'Permissions', 'Expiration', 'Use Case']}
rows={[
[
<InlineCode key="type">Master Key</InlineCode>,
'Full admin access, can create/revoke keys',
<span key="exp" className="text-green-400">Never expires</span>,
'Server-side admin operations, key management',
],
[
<InlineCode key="type" color="success">Project Key</InlineCode>,
'Image generation only',
<span key="exp" className="text-amber-400">90 days</span>,
'Application integration, API requests',
],
]}
/>
<div className="mt-6">
<TipBox variant="prominent" type="warning">
<strong className="text-amber-300">Master Key Security:</strong> Master keys have full
administrative access and never expire. Store them securely in encrypted vaults or
secret managers. Never expose master keys in client-side code, logs, or version control.
Use project keys for application integration whenever possible.
</TipBox>
</div>
</div>
<div id="creating-keys" className="mb-8">
<SectionHeader level={3} id="creating-keys" className="mb-4">
Creating Keys
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
For first-time setup, use the bootstrap endpoint to create your initial master key:
</p>
<CodeBlock
code={`# Bootstrap your first master key (one-time only)
curl -X POST https://api.banatie.com/api/bootstrap/initial-key
# Response
{
"success": true,
"data": {
"key": "bnt_master_abc123...",
"type": "master"
}
}`}
language="bash"
filename="Bootstrap Master Key"
/>
<div className="mt-6">
<p className="text-gray-300 leading-relaxed mb-4">
Once you have a master key, you can create project keys for your applications:
</p>
<CodeBlock
code={`# Create a project key with master key authentication
curl -X POST https://api.banatie.com/api/admin/keys \\
-H "X-API-Key: YOUR_MASTER_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"type": "project",
"projectId": "my-app",
"name": "Production API Key"
}'
# Response
{
"success": true,
"data": {
"key": "bnt_project_xyz789...",
"type": "project",
"expiresAt": "2025-04-14T00:00:00Z"
}
}`}
language="bash"
filename="Create Project Key"
/>
</div>
</div>
</section>
{/* Using API Keys */}
<section id="using-keys" className="mb-12">
<SectionHeader level={2} id="using-keys" className="mb-4">
Using API Keys
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Include your API key in the <InlineCode>X-API-Key</InlineCode> header
with every request:
</p>
<CodeBlock
code={`# Example: Generate an image with project key
curl -X POST https://api.banatie.com/api/text-to-image \\
-H "X-API-Key: bnt_project_xyz789..." \\
-H "Content-Type: application/json" \\
-d '{
"prompt": "a sunset over the ocean",
"aspectRatio": "16:9"
}'`}
language="bash"
filename="Authenticated Request"
/>
<div className="mt-6">
<TipBox variant="compact" type="info">
<strong>Environment Variables:</strong> Store API keys in environment variables, not
hardcoded in your application. Example:{' '}
<InlineCode>BANATIE_API_KEY</InlineCode>
</TipBox>
</div>
</section>
{/* Rate Limits */}
<section id="rate-limits" className="mb-12">
<SectionHeader level={2} id="rate-limits" className="mb-6">
Rate Limits
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
API keys are subject to rate limits to ensure fair usage and system stability. Limits
vary by key type and plan tier:
</p>
<Table
headers={['Key Type', 'Rate Limit', 'Reset Window', 'Upgrade Available']}
rows={[
[
<InlineCode key="type">Master Key</InlineCode>,
<span key="limit" className="text-green-400">Unlimited</span>,
'N/A',
'N/A',
],
[
<InlineCode key="type" color="success">Project Key (Free)</InlineCode>,
<span key="limit" className="text-amber-400">100 requests/hour</span>,
'1 hour rolling',
<a key="upgrade" href="/pricing" className="text-purple-400 hover:underline">Yes</a>,
],
[
<InlineCode key="type" color="success">Project Key (Pro)</InlineCode>,
<span key="limit" className="text-green-400">1,000 requests/hour</span>,
'1 hour rolling',
<a key="upgrade" href="/pricing" className="text-purple-400 hover:underline">Yes</a>,
],
]}
/>
<div className="mt-6">
<p className="text-gray-300 leading-relaxed mb-4">
When you exceed rate limits, the API returns a <InlineCode color="error">429 Too Many Requests</InlineCode> status.
Check the response headers for retry timing:
</p>
<CodeBlock
code={`# Rate limit response headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704153600
# Error response body
{
"error": "Rate limit exceeded",
"retryAfter": 3600
}`}
language="bash"
filename="Rate Limit Response"
/>
</div>
</section>
{/* Security Best Practices */}
<section id="security" className="mb-12">
<SectionHeader level={2} id="security" className="mb-6">
Security Best Practices
</SectionHeader>
<TipBox variant="prominent" type="warning">
<strong className="text-amber-300">Critical Security Guidelines:</strong>
<ul className="mt-3 space-y-2 list-disc list-inside">
<li>Never commit API keys to version control systems (Git, SVN, etc.)</li>
<li>Store keys in environment variables or secret management services</li>
<li>Use project keys in applications, reserve master keys for admin operations</li>
<li>Rotate keys regularly, especially after team member changes</li>
<li>Implement server-side API calls for production applications</li>
<li>Monitor API key usage in your dashboard for suspicious activity</li>
</ul>
</TipBox>
<div className="mt-6">
<h3 className="text-lg font-semibold text-white mb-3">Key Rotation Example</h3>
<CodeBlock
code={`# 1. Create new project key
curl -X POST https://api.banatie.com/api/admin/keys \\
-H "X-API-Key: YOUR_MASTER_KEY" \\
-d '{"type": "project", "projectId": "my-app", "name": "New Key"}'
# 2. Update your application to use new key
export BANATIE_API_KEY="bnt_project_new..."
# 3. Revoke old key
curl -X DELETE https://api.banatie.com/api/admin/keys/OLD_KEY_ID \\
-H "X-API-Key: YOUR_MASTER_KEY"`}
language="bash"
filename="Key Rotation Process"
/>
</div>
</section>
</DocPage>
);
}

View File

@ -0,0 +1,75 @@
'use client';
import { ReactNode } from 'react';
import { usePathname } from 'next/navigation';
import { SubsectionNav } from '@/components/shared/SubsectionNav';
import { DocsSidebar } from '@/components/docs/layout/DocsSidebar';
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
/**
* Root Documentation Layout
*
* Provides shared layout elements for all documentation pages:
* - SubsectionNav at the top
* - Three-column layout (left sidebar + content + right TOC via DocPage)
* - Background gradients
*
* Uses ThreeColumnLayout for consistent column structure:
* - Left: DocsSidebar (w-64, hidden lg:block)
* - Center: Page content (flex-1)
* - Right: Handled by DocPage component
*
* Pages handle their own:
* - Breadcrumbs (manually specified)
* - Article content
* - TOC sidebar (on the right via DocPage)
*
* Features:
* - Animated gradient background matching landing page
* - Automatic active page detection via pathname
* - Responsive layout (mobile tablet desktop)
*/
interface DocsRootLayoutProps {
children: ReactNode;
}
const navItems = [
{ label: 'Documentation', href: '/docs' },
{ label: 'Demo', href: '/demo' },
{ label: 'Examples', href: '/docs/examples' },
];
export default function DocsRootLayout({ children }: DocsRootLayoutProps) {
const pathname = usePathname();
return (
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950">
{/* Animated gradient background (matching landing page) */}
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div className="absolute top-1/4 -left-1/4 w-96 h-96 bg-purple-600/10 rounded-full blur-3xl animate-pulse"></div>
<div className="absolute bottom-1/4 -right-1/4 w-96 h-96 bg-cyan-600/10 rounded-full blur-3xl animate-pulse delay-700"></div>
</div>
{/* Subsection Navigation */}
<SubsectionNav
items={navItems}
currentPath={pathname}
ctaText="Join Beta"
ctaHref="/signup"
/>
{/* Three-column Documentation Layout */}
<div className="relative z-10">
<ThreeColumnLayout
left={
<div className="border-r border-white/10 bg-slate-950/50 backdrop-blur-sm sticky top-12 h-[calc(100vh-3rem)] overflow-y-auto">
<DocsSidebar currentPath={pathname} />
</div>
}
center={children}
/>
</div>
</div>
);
}

View File

@ -0,0 +1,196 @@
'use client';
/**
* Getting Started Page - Production Documentation
*
* Refactored to use DocPage component for consistent layout
* Layout handles SubsectionNav and Left Sidebar
* DocPage handles Breadcrumb, Article structure, Next Steps, and TOC
* This page provides only the content (Hero + sections)
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import {
Hero,
SectionHeader,
ResponseBlock,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'introduction', text: 'Introduction', level: 2 },
{ id: 'quick-start', text: 'Quick Start', level: 2 },
{ id: 'installation', text: 'Installation', level: 3 },
{ id: 'authentication', text: 'Authentication', level: 3 },
{ id: 'first-request', text: 'Your First Request', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
export default function GettingStartedPage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'Getting Started' },
]}
tocItems={tocItems}
nextSteps={{
description:
'Now that you have generated your first image, explore these resources to build more advanced integrations:',
links: [
{
href: '/docs/api/text-to-image',
title: 'API Reference',
description: 'Explore all available endpoints, parameters, and response formats.',
accent: 'primary',
},
{
href: '/docs/guides/authentication',
title: 'Authentication Guide',
description: 'Learn about API keys, rate limits, and security best practices.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Getting Started"
subtitle="Welcome to the Banatie API documentation. Learn how to integrate AI-powered image generation into your applications in minutes."
/>
{/* Introduction */}
<section id="introduction" className="mb-12">
<SectionHeader level={2} id="introduction">
Introduction
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Banatie is a developer-first API for AI-powered image generation. Built on Google Gemini
2.5 Flash and Imagen 4.0, it transforms text prompts and reference images into
production-ready visuals.
</p>
<p className="text-gray-300 leading-relaxed mb-6">
Whether you are building a content creation platform, e-commerce site, or creative tool,
Banatie provides the infrastructure you need to generate high-quality images at scale.
</p>
{/* Compact Tip Box */}
<TipBox variant="compact" type="info">
<strong>New to API integration?</strong> Start with our{' '}
<a href="/docs/examples" className="text-purple-400 hover:underline">
code examples
</a>{' '}
to see common use cases in action.
</TipBox>
</section>
{/* Quick Start */}
<section id="quick-start" className="mb-12">
<SectionHeader level={2} id="quick-start" className="mb-6">
Quick Start
</SectionHeader>
<div id="installation" className="mb-8">
<SectionHeader level={3} id="installation">
Installation
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Banatie is a REST API, so you do not need to install any libraries. However, we provide
SDKs for popular languages to make integration easier.
</p>
<CodeBlock
code={`# Using npm (JavaScript/TypeScript)
npm install @banatie/sdk
# Using pip (Python)
pip install banatie
# Using Go
go get github.com/banatie/sdk-go`}
language="bash"
filename="Installation"
/>
</div>
<div id="authentication" className="mb-8">
<SectionHeader level={3} id="authentication">
Authentication
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
All API requests require an API key. You can create an API key from your dashboard or
using the bootstrap endpoint for initial setup.
</p>
{/* Prominent Tip Box for Security Warning */}
<div className="mb-6">
<TipBox variant="prominent" type="warning">
<strong className="text-amber-300">Security Best Practice:</strong> Keep your API keys secure.
Never commit them to public repositories or expose them in client-side code. Use environment
variables and server-side implementations for production applications.
</TipBox>
</div>
<CodeBlock
code={`# Create your first API key (one-time bootstrap)
curl -X POST https://api.banatie.com/api/bootstrap/initial-key
# Save the returned key securely
export BANATIE_API_KEY="bnt_your_key_here"`}
language="bash"
filename="Get API Key"
/>
</div>
</section>
{/* First Request */}
<section id="first-request" className="mb-12">
<SectionHeader level={2} id="first-request">
Your First Request
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Let's generate your first image! This example uses curl, but you can use any HTTP client
or our SDKs.
</p>
<CodeBlock
code={`curl -X POST https://api.banatie.com/api/text-to-image \\
-H "X-API-Key: YOUR_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"prompt": "a serene mountain landscape at sunset",
"filename": "mountain_sunset",
"aspectRatio": "16:9",
"autoEnhance": true
}'`}
language="bash"
filename="Generate Image"
/>
<div className="mt-6">
<p className="text-sm font-semibold text-gray-300 mb-3">Expected Response:</p>
<ResponseBlock
status="success"
statusCode={200}
statusLabel="✓ 200 Success"
content={`{
"success": true,
"data": {
"url": "https://cdn.banatie.com/org/project/generated/2025-01/mountain_sunset.png",
"filepath": "org/project/generated/2025-01/mountain_sunset.png",
"width": 1920,
"height": 1080,
"promptEnhancement": {
"enhancedPrompt": "A breathtaking mountain landscape..."
}
}
}`}
/>
</div>
</section>
</DocPage>
);
}

View File

@ -62,3 +62,40 @@ body {
html { html {
scroll-behavior: smooth; scroll-behavior: smooth;
} }
/* Custom Scrollbars for Dark Theme */
/* Firefox */
* {
scrollbar-width: thin;
scrollbar-color: rgb(71, 85, 105) rgb(15, 23, 42); /* slate-600 on slate-950 */
}
/* Webkit browsers (Chrome, Safari, Edge) */
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-track {
background: rgb(15, 23, 42); /* slate-950 */
border-radius: 4px;
}
*::-webkit-scrollbar-thumb {
background: rgb(71, 85, 105); /* slate-600 */
border-radius: 4px;
border: 2px solid rgb(15, 23, 42); /* slate-950 */
}
*::-webkit-scrollbar-thumb:hover {
background: rgb(100, 116, 139); /* slate-500 */
}
/* Code blocks - slightly brighter scrollbar for better visibility */
pre::-webkit-scrollbar-thumb {
background: rgb(100, 116, 139); /* slate-500 */
}
pre::-webkit-scrollbar-thumb:hover {
background: rgb(148, 163, 184); /* slate-400 */
}

View File

@ -0,0 +1,202 @@
'use client';
/**
* EndpointCard Component
*
* A structured display block for API endpoint information including HTTP method,
* path, and base URL. Creates consistent, scannable API reference documentation.
*
* ## Design Principles
*
* 1. **Information Grouping**: Related endpoint data in one visual container
* - Method badge + endpoint path on same line for scannability
* - Base URL separated below for clarity without clutter
* - Container styling creates clear boundaries
*
* 2. **Visual Hierarchy**: Emphasis guides attention naturally
* - HTTP method badge draws eye first (color + position)
* - Endpoint path is prominent (white text, slightly larger)
* - Base URL is secondary (smaller, muted gray)
*
* 3. **Technical Precision**: Monospace fonts for code accuracy
* - Endpoint path uses code styling
* - Prevents ambiguity in technical content
* - Familiar to developers
*
* ## Layout Strategy
*
* Horizontal Flow:
* ```
* [METHOD BADGE] /api/endpoint-path
* Base URL: https://api.example.com
* ```
*
* This layout:
* - Puts most important info (method + path) on top line
* - Creates natural left-to-right reading flow
* - Groups related information visually
* - Works well at various screen sizes
*
* ## Container Styling
*
* Background & Border:
* - bg-slate-900/50: Semi-transparent dark background
* - Separates from page background
* - Maintains dark theme consistency
* - Subtle enough not to dominate
*
* - border-slate-700: Defined edges
* - Creates clear container boundaries
* - Adds structure without heaviness
* - Professional, technical appearance
*
* - rounded-xl: Soft corners (12px radius)
* - Modern, friendly appearance
* - Matches other card elements
* - Balances technical precision with approachability
*
* Padding:
* - p-4 (16px): Comfortable breathing room
* - Prevents cramped appearance
* - Makes content easy to read
* - Balances whitespace and content
*
* ## Typography System
*
* Endpoint Path:
* - text-sm: Slightly smaller than body but clearly readable
* - text-white: Maximum contrast for primary content
* - font-mono implicitly via <code>: Technical accuracy
*
* Base URL:
* - text-xs: Clearly secondary information
* - text-gray-400: Muted to not compete with endpoint
* - Includes "Base URL:" label for context
*
* ## Spacing & Layout
*
* Flex Layout Benefits:
* - items-center: Vertically centers badge with text
* - gap-3 (12px): Comfortable space between badge and path
* - mb-2 (8px): Separates primary info from base URL
*
* ## Usage Examples
*
* ```tsx
* // Standard REST endpoint
* <EndpointCard
* method="POST"
* endpoint="/api/text-to-image"
* baseUrl="https://api.banatie.com"
* />
*
* // Different HTTP methods
* <EndpointCard
* method="GET"
* endpoint="/api/images"
* />
*
* <EndpointCard
* method="DELETE"
* endpoint="/api/admin/keys/:keyId"
* />
*
* // Custom base URL
* <EndpointCard
* method="PUT"
* endpoint="/api/projects/:id"
* baseUrl="https://api.custom-domain.com"
* />
* ```
*
* ## Content Guidelines
*
* HTTP Method:
* - Always uppercase (GET, POST, PUT, DELETE, PATCH)
* - Choose semantically correct method
* - Follows REST conventions
*
* Endpoint Path:
* - Start with forward slash (/)
* - Use lowercase with hyphens (kebab-case)
* - Include path parameters with colon prefix (:id, :keyId)
* - Be specific and descriptive
* - Example: `/api/projects/:projectId/keys`
*
* Base URL:
* - Include protocol (https://)
* - Use production URL as default
* - No trailing slash
* - Example: `https://api.banatie.com`
*
* ## Accessibility
*
* - Semantic HTML structure
* - Proper contrast ratios for all text
* - MethodBadge includes aria-label
* - Logical reading order (method path base)
* - Keyboard accessible (no interactive elements, but focusable context)
*
* ## Visual Language
*
* The EndpointCard establishes:
* - Professional, technical aesthetic
* - Consistent API documentation pattern
* - Clear, unambiguous endpoint identification
* - Easy copy-paste of endpoint information
* - Quick visual scanning in multi-endpoint docs
*
* @component
* @example
* <EndpointCard
* method="POST"
* endpoint="/api/text-to-image"
* baseUrl="https://api.banatie.com"
* />
*/
import { MethodBadge, HttpMethod } from './MethodBadge';
/**
* Props for the EndpointCard component
*/
export interface EndpointCardProps {
/** HTTP method for the endpoint */
method: HttpMethod;
/** API endpoint path (e.g., "/api/users") */
endpoint: string;
/** Base URL for the API (default: "https://api.banatie.com") */
baseUrl?: string;
/** Optional CSS class name for additional styling */
className?: string;
}
export const EndpointCard = ({
method,
endpoint,
baseUrl = 'https://api.banatie.com',
className = '',
}: EndpointCardProps) => {
return (
<div
className={`
p-4
bg-slate-900/50
border border-slate-700
rounded-xl
${className}
`}
>
{/* Method + Endpoint Row */}
<div className="flex items-center gap-3 mb-2">
<MethodBadge method={method} />
<code className="text-sm text-white">{endpoint}</code>
</div>
{/* Base URL */}
<p className="text-xs text-gray-400">
Base URL: <code className="text-purple-400">{baseUrl}</code>
</p>
</div>
);
};

View File

@ -0,0 +1,185 @@
'use client';
/**
* Hero Component
*
* The primary heading section at the top of documentation pages.
* Establishes visual hierarchy and sets expectations for page content.
*
* ## Design Principles
*
* 1. **Information Hierarchy**: Clear progression from title to description
* - Large, bold title commands attention immediately
* - Subtitle provides context without competing with title
* - Consistent spacing creates rhythm and flow
*
* 2. **Typography Scale**: Responsive sizing for all devices
* - Mobile-first approach with thoughtful breakpoints
* - Maintains readability across screen sizes
* - Proper line-height for comfortable reading
*
* 3. **Visual Weight**: Balanced presence without overwhelming
* - White title for maximum contrast and impact
* - Gray subtitle for supporting, not competing
* - Generous margins create breathing room
*
* ## Size Variants
*
* @param {'default'} default - Standard documentation pages
* - Title: text-4xl (36px) md:text-5xl (48px)
* - Use cases: API references, guides, tutorials
* - Psychology: Professional, focused, informative
* - Spacing: mb-12 (48px) after hero
*
* @param {'large'} large - Major landing pages, section introductions
* - Title: text-5xl (48px) md:text-6xl (60px)
* - Use cases: Documentation home, major category pages
* - Psychology: Welcoming, important, primary entry point
* - Spacing: mb-16 (64px) after hero
*
* ## Typographic Hierarchy
*
* Title Characteristics:
* - **Weight**: font-bold (700) - Commands attention
* - **Color**: text-white - Maximum contrast, immediately readable
* - **Spacing**: mb-4 (16px) - Connects title to subtitle
*
* Subtitle Characteristics:
* - **Size**: text-xl (20px) - Large enough to be comfortable, small enough to defer
* - **Weight**: Regular (400) - Contrast with bold title
* - **Color**: text-gray-400 - Supporting role, doesn't compete
* - **Line height**: leading-relaxed (1.625) - Comfortable multi-line reading
*
* ## Responsive Behavior
*
* Mobile (<768px):
* - Smaller text sizes to fit narrow screens
* - Maintains readability with appropriate line lengths
* - Single column layout
*
* Desktop (768px):
* - Larger text leverages available space
* - Enhanced visual impact
* - Better use of vertical rhythm
*
* ## Visual Language
*
* The hero serves multiple purposes in documentation:
* 1. **Orientation**: Tells users where they are
* 2. **Context**: Explains what this page covers
* 3. **Hierarchy**: Establishes importance through size
* 4. **Consistency**: Familiarpattern across all docs pages
*
* Color Psychology:
* - **White title**: Authority, clarity, primary information
* - **Gray subtitle**: Support, context, secondary information
* - Creates natural reading flow: bold light, primary secondary
*
* ## Accessibility
*
* - Uses semantic HTML (<h1> for title)
* - Proper heading hierarchy (h1 is first heading on page)
* - Sufficient color contrast (white on dark background)
* - Readable font sizes (minimum 20px for subtitle)
* - Appropriate line-height for comfortable reading
*
* ## Usage Examples
*
* ```tsx
* // Standard API reference page
* <Hero
* title="Text to Image"
* subtitle="Generate high-quality images from text prompts using AI-powered models."
* />
*
* // Documentation home page
* <Hero
* size="large"
* title="Welcome to Banatie API Documentation"
* subtitle="Learn how to integrate AI-powered image generation into your applications in minutes."
* />
*
* // Guide page
* <Hero
* title="Authentication"
* subtitle="Learn how to authenticate with the Banatie API using API keys, manage rate limits, and implement security best practices."
* />
* ```
*
* ## Content Guidelines
*
* Title:
* - Keep short and specific (1-5 words ideal)
* - Use sentence case (capitalize first word only)
* - Avoid punctuation unless necessary
* - Match page's primary purpose
*
* Subtitle:
* - One to two sentences maximum
* - Explain what user will learn or accomplish
* - Use active voice and present tense
* - End with period for complete thoughts
*
* @component
* @example
* <Hero
* title="Getting Started"
* subtitle="Welcome to the Banatie API documentation."
* size="default"
* />
*/
import { ReactNode } from 'react';
/**
* Size variant determining the visual scale of the hero
*/
export type HeroSize = 'default' | 'large';
/**
* Props for the Hero component
*/
export interface HeroProps {
/** The main page title (renders as h1) */
title: string;
/** Supporting description or context */
subtitle: string | ReactNode;
/** Size variant (default: 'default') */
size?: HeroSize;
/** Optional CSS class name for additional styling */
className?: string;
}
/**
* Title size system mapping size variant to responsive text classes
*/
const titleSizes: Record<HeroSize, string> = {
default: 'text-4xl md:text-5xl',
large: 'text-5xl md:text-6xl',
};
/**
* Spacing system for bottom margin
*/
const spacingSizes: Record<HeroSize, string> = {
default: 'mb-12',
large: 'mb-16',
};
export const Hero = ({
title,
subtitle,
size = 'default',
className = '',
}: HeroProps) => {
return (
<div className={`${spacingSizes[size]} ${className}`}>
<h1 className={`${titleSizes[size]} font-bold text-white mb-4`}>
{title}
</h1>
<p className="text-xl text-gray-400 leading-relaxed">
{subtitle}
</p>
</div>
);
};

View File

@ -0,0 +1,156 @@
'use client';
/**
* InlineCode Component
*
* A consistent inline code element for technical content within documentation text.
* Provides semantic color variants to emphasize context and meaning.
*
* ## Design Principles
*
* 1. **Readability First**: High contrast with surrounding text
* - Dark background distinguishes code from prose
* - Slightly larger padding creates breathing room
* - Rounded corners soften appearance for inline usage
*
* 2. **Semantic Coloring**: Color conveys meaning beyond monospace
* - Different contexts require different visual emphasis
* - Colors match broader design system for consistency
* - Never rely on color alone (always has monospace + background)
*
* 3. **Typography**: Monospace font signals technical content
* - Built-in font-mono class ensures code appearance
* - Small size (text-sm) fits inline with body text
* - Maintains readability without disrupting flow
*
* ## Color Semantic Values
*
* @param {'primary'} primary - Default technical content, variable names, general code
* - Visual: Purple accent (matches brand primary)
* - Psychology: Neutral technical content, primary focus
* - Use cases: `apiKey`, `userId`, general inline code snippets
* - When to use: Default choice for any code reference
*
* @param {'success'} success - Positive examples, correct values, successful states
* - Visual: Green accent
* - Psychology: Correct, recommended, successful outcome
* - Use cases: `status: 200`, `"success": true`, correct API responses
* - When to use: Showing what TO do, expected good outcomes
*
* @param {'error'} error - Error values, incorrect usage, warnings
* - Visual: Red accent
* - Psychology: Wrong, dangerous, avoid
* - Use cases: `status: 500`, error codes, anti-patterns
* - When to use: Showing what NOT to do, error states
*
* @param {'neutral'} neutral - Informational, documentation, placeholders
* - Visual: Slate/Gray (matches text color scheme)
* - Psychology: Reference, documentation, less emphasis
* - Use cases: HTTP headers like `X-API-Key`, endpoints, technical terms
* - When to use: Supporting information, less critical code references
*
* ## Visual Language
*
* Background Strategy:
* - Dark bg (slate-800) creates clear separation from surrounding text
* - Works on both light and dark documentation themes
* - Sufficient contrast meets WCAG AA standards
*
* Padding Strategy:
* - Horizontal: px-1.5 (6px) - enough space but not disruptive
* - Vertical: py-0.5 (2px) - maintains inline text flow
* - Border radius: rounded (4px) - soft enough for inline, defined enough for blocks
*
* Font Strategy:
* - text-sm: Slightly smaller than body text but still readable
* - font-mono: Clear distinction from prose, signals code
* - font-normal: Not bold, integrates naturally with surrounding text
*
* ## Usage Examples
*
* ```tsx
* // Default technical content
* Set your <InlineCode>API_KEY</InlineCode> environment variable.
*
* // With semantic colors
* The response includes <InlineCode color="success">status: 200</InlineCode>.
* Avoid using <InlineCode color="error">hardcoded credentials</InlineCode>.
* Include the <InlineCode color="neutral">X-API-Key</InlineCode> header.
*
* // Parameter documentation
* The <InlineCode>autoEnhance</InlineCode> parameter defaults to false.
*
* // Error messages
* If you receive <InlineCode color="error">401 Unauthorized</InlineCode>, check your API key.
* ```
*
* ## Composition Guidelines
*
* DO:
* - Use for short code snippets (1-4 words)
* - Use for technical terms, variable names, values
* - Use semantic colors to reinforce meaning
* - Place naturally within sentences
*
* DON'T:
* - Use for multi-line code (use CodeBlock instead)
* - Use for long strings (creates readability issues)
* - Overuse color variants (primary is default for a reason)
* - Use as a design element (it's for code content only)
*
* @component
* @example
* <InlineCode color="primary">apiKey</InlineCode>
*/
import { ReactNode } from 'react';
/**
* Semantic color variants for inline code
*/
export type InlineCodeColor = 'primary' | 'success' | 'error' | 'neutral';
/**
* Props for the InlineCode component
*/
export interface InlineCodeProps {
/** The code content to display */
children: ReactNode;
/** Semantic color variant (default: 'primary') */
color?: InlineCodeColor;
/** Optional CSS class name for additional styling */
className?: string;
}
/**
* Color system mapping semantic meanings to text colors
* All use same dark background for consistency
*/
const colorStyles: Record<InlineCodeColor, string> = {
primary: 'text-purple-400',
success: 'text-green-400',
error: 'text-red-400',
neutral: 'text-gray-400',
};
export const InlineCode = ({
children,
color = 'primary',
className = '',
}: InlineCodeProps) => {
return (
<code
className={`
inline-block
px-1.5 py-0.5
bg-slate-800
rounded
font-mono text-sm
${colorStyles[color]}
${className}
`}
>
{children}
</code>
);
};

View File

@ -0,0 +1,502 @@
'use client';
/**
* Interactive API Widget - Production Version
*
* Minimized layout with clean design system:
* - No inline API key input (uses DocsApiKeyInput component)
* - Clean header with method badge, endpoint, and expand button
* - Full-width code snippet area
* - Compact footer with API key anchor link and Try It button
* - Expanded view opens full-screen modal with code + response side-by-side
*/
import { useState, useEffect } from 'react';
interface InteractiveAPIWidgetProps {
endpoint: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
description: string;
parameters?: Array<{
name: string;
type: string;
required: boolean;
description: string;
defaultValue?: string;
}>;
}
type Language = 'curl' | 'javascript' | 'python' | 'go' | 'js-sdk' | 'mcp';
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
const API_KEY_STORAGE = 'banatie_docs_api_key';
const getMethodBadgeStyle = (method: string): string => {
switch (method) {
case 'GET':
return 'bg-cyan-600/20 text-cyan-400 border border-cyan-500/40';
case 'POST':
return 'bg-green-600/20 text-green-400 border border-green-500/40';
case 'PUT':
return 'bg-amber-600/20 text-amber-400 border border-amber-500/40';
case 'DELETE':
return 'bg-red-600/20 text-red-400 border border-red-500/40';
default:
return 'bg-slate-600/20 text-slate-400 border border-slate-500/40';
}
};
export const InteractiveAPIWidget = ({
endpoint,
method,
description,
parameters = [],
}: InteractiveAPIWidgetProps) => {
const [language, setLanguage] = useState<Language>('curl');
const [apiKey, setApiKey] = useState('');
const [isExecuting, setIsExecuting] = useState(false);
const [response, setResponse] = useState<any>(null);
const [error, setError] = useState<string | null>(null);
const [isExpanded, setIsExpanded] = useState(false);
const [paramValues, setParamValues] = useState<Record<string, string>>({});
// Load API key from localStorage and listen for changes
useEffect(() => {
const loadApiKey = () => {
const stored = localStorage.getItem(API_KEY_STORAGE);
if (stored) setApiKey(stored);
};
loadApiKey();
// Listen for API key changes from DocsApiKeyInput
const handleApiKeyChange = (e: CustomEvent) => {
setApiKey(e.detail);
};
window.addEventListener('apiKeyChanged', handleApiKeyChange as EventListener);
// Initialize parameter defaults
const defaults: Record<string, string> = {
prompt: 'a futuristic city at sunset',
filename: 'demo',
aspectRatio: '16:9',
};
parameters.forEach((param) => {
if (param.defaultValue) {
defaults[param.name] = param.defaultValue;
}
});
setParamValues(defaults);
return () => {
window.removeEventListener('apiKeyChanged', handleApiKeyChange as EventListener);
};
}, [parameters]);
// Get language display name
const getLanguageLabel = (lang: Language): string => {
switch (lang) {
case 'curl':
return 'cURL';
case 'javascript':
return 'JavaScript';
case 'python':
return 'Python';
case 'go':
return 'Go';
case 'js-sdk':
return 'JS SDK';
case 'mcp':
return 'MCP';
}
};
// Generate code examples
const generateCode = (): string => {
const url = `${API_BASE_URL}${endpoint}`;
// Coming soon placeholders
if (language === 'js-sdk' || language === 'mcp') {
return `# Coming soon\n# ${getLanguageLabel(language)} integration is under development`;
}
switch (language) {
case 'curl':
return `curl -X ${method} "${url}" \\
-H "X-API-Key: ${apiKey || 'YOUR_API_KEY'}" \\
-H "Content-Type: application/json" \\
-d '{
"prompt": "a futuristic city at sunset",
"filename": "city",
"aspectRatio": "16:9"
}'`;
case 'javascript':
return `const response = await fetch('${url}', {
method: '${method}',
headers: {
'X-API-Key': '${apiKey || 'YOUR_API_KEY'}',
'Content-Type': 'application/json'
},
body: JSON.stringify({
prompt: 'a futuristic city at sunset',
filename: 'city',
aspectRatio: '16:9'
})
});
const data = await response.json();
console.log(data);`;
case 'python':
return `import requests
url = '${url}'
headers = {
'X-API-Key': '${apiKey || 'YOUR_API_KEY'}',
'Content-Type': 'application/json'
}
data = {
'prompt': 'a futuristic city at sunset',
'filename': 'city',
'aspectRatio': '16:9'
}
response = requests.${method.toLowerCase()}(url, headers=headers, json=data)
print(response.json())`;
case 'go':
return `package main
import (
"bytes"
"encoding/json"
"net/http"
)
func main() {
url := "${url}"
data := map[string]interface{}{
"prompt": "a futuristic city at sunset",
"filename": "city",
"aspectRatio": "16:9",
}
jsonData, _ := json.Marshal(data)
req, _ := http.NewRequest("${method}", url, bytes.NewBuffer(jsonData))
req.Header.Set("X-API-Key", "${apiKey || 'YOUR_API_KEY'}")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
}`;
default:
return '';
}
};
// Execute API request
const executeRequest = async () => {
if (!apiKey) {
setError('Please enter your API key in the top-right corner');
return;
}
setIsExecuting(true);
setError(null);
setResponse(null);
try {
const body = {
prompt: paramValues.prompt || 'a futuristic city at sunset',
filename: paramValues.filename || 'demo',
aspectRatio: paramValues.aspectRatio || '16:9',
};
console.log('🔵 API Request:', {
url: `${API_BASE_URL}${endpoint}`,
method,
headers: { 'X-API-Key': apiKey.substring(0, 10) + '...' },
body,
});
const res = await fetch(`${API_BASE_URL}${endpoint}`, {
method,
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
},
body: method !== 'GET' ? JSON.stringify(body) : undefined,
});
console.log('🟢 Response Status:', res.status, res.statusText);
if (!res.ok) {
let errorMessage = `HTTP ${res.status}: ${res.statusText}`;
try {
const errorData = await res.json();
errorMessage = errorData.message || errorData.error || errorMessage;
} catch {
// Response is not JSON
}
throw new Error(errorMessage);
}
const data = await res.json();
console.log('✅ Response Data:', data);
setResponse(data);
} catch (err) {
console.error('❌ API Error:', err);
const errorMsg = err instanceof Error ? err.message : 'Failed to execute request';
setError(`Request failed: ${errorMsg}`);
} finally {
setIsExecuting(false);
}
};
// Copy code to clipboard
const copyCode = () => {
navigator.clipboard.writeText(generateCode());
};
// Expand API key input in navigation
const expandApiKey = () => {
window.dispatchEvent(new CustomEvent('expandApiKeyInput'));
};
const isSuccess = response && response.success === true;
return (
<>
{/* Minimized Widget */}
<div className="my-8 bg-slate-900/50 backdrop-blur-sm border border-slate-700/50 rounded-2xl overflow-hidden">
{/* Header */}
<div className="flex items-center justify-between bg-slate-950/80 px-6 py-4 border-b border-slate-700/50">
<div className="flex items-center gap-3">
<div className={`px-3 py-1 rounded text-xs font-bold ${getMethodBadgeStyle(method)}`}>
{method}
</div>
<code className="text-sm text-gray-400">{endpoint}</code>
</div>
<button
onClick={() => setIsExpanded(true)}
className="flex items-center gap-2 px-4 py-2 bg-cyan-600/10 hover:bg-cyan-600/20 text-cyan-400 rounded-lg transition-all text-sm font-medium border border-cyan-500/30 hover:border-cyan-500/50"
>
<span></span>
<span>Expand</span>
</button>
</div>
{/* Code Section - Full Width */}
<div className="p-6">
{/* Language Tabs */}
<div className="flex items-center gap-2 mb-4 flex-wrap">
{(['curl', 'javascript', 'python', 'go', 'js-sdk', 'mcp'] as Language[]).map((lang) => (
<button
key={lang}
onClick={() => setLanguage(lang)}
className={`px-3 py-1.5 text-xs rounded-lg font-medium transition-all border ${
language === lang
? 'bg-slate-700 text-white border-slate-600'
: 'bg-slate-800/50 text-gray-400 border-slate-700/50 hover:border-slate-600 hover:text-gray-300'
}`}
>
{getLanguageLabel(lang)}
</button>
))}
</div>
{/* Code Display */}
<div className="border border-slate-700/50 rounded-xl overflow-hidden">
<div className="flex items-center justify-between bg-slate-950/80 px-4 py-3 border-b border-slate-700/50">
<span className="text-xs text-gray-400 font-medium">Code Example</span>
<button
onClick={copyCode}
className="text-xs text-gray-400 hover:text-white transition-colors font-medium"
>
Copy
</button>
</div>
<div className="p-4 bg-slate-950/50 max-h-80 overflow-auto">
<pre className="text-sm text-gray-300 leading-relaxed">
<code>{generateCode()}</code>
</pre>
</div>
</div>
</div>
{/* Footer */}
<div className="flex items-center justify-between px-6 pb-6">
<button
onClick={expandApiKey}
className="text-sm text-gray-400 hover:text-white underline-offset-4 hover:underline transition-colors"
>
{apiKey ? 'API Key Set' : 'Enter API Key'}
</button>
<button
onClick={executeRequest}
disabled={!apiKey || isExecuting}
className="px-4 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 text-white text-sm font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
{isExecuting ? 'Executing...' : 'Try It'}
</button>
</div>
{/* Inline Response (if any) */}
{(response || error) && (
<div className="border-t border-slate-700/50 p-6 bg-slate-950/50">
<h4 className="text-sm font-semibold text-white mb-3">
Response
</h4>
{error ? (
<div className="p-4 bg-red-500/10 border border-red-500/40 rounded-xl text-red-400 text-sm">
<p className="font-semibold mb-1">Error</p>
<p className="text-xs">{error}</p>
</div>
) : (
<div className={`p-4 rounded-xl ${
isSuccess
? 'bg-green-500/5 border border-green-500/30'
: 'bg-slate-800/50 border border-slate-700'
}`}>
{isSuccess && (
<div className="flex items-center gap-2 mb-3">
<span className="px-2 py-1 text-xs bg-green-500/20 text-green-400 rounded border border-green-500/40">
200 Success
</span>
</div>
)}
<pre className="text-xs text-gray-300 overflow-x-auto max-h-60">
<code>{JSON.stringify(response, null, 2)}</code>
</pre>
</div>
)}
</div>
)}
</div>
{/* Expanded Modal */}
{isExpanded && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm">
<div className="relative w-[90vw] h-[85vh] bg-slate-900 border border-slate-700/50 rounded-2xl shadow-2xl overflow-hidden flex flex-col">
{/* Modal Header */}
<div className="flex items-center justify-between bg-slate-950/80 px-6 py-4 border-b border-slate-700/50">
<div className="flex items-center gap-4">
<div className="flex items-center gap-3">
<div className={`px-3 py-1 rounded text-xs font-bold ${getMethodBadgeStyle(method)}`}>
{method}
</div>
<code className="text-sm text-gray-400">{endpoint}</code>
</div>
<span className="text-xs text-gray-500"></span>
<p className="text-xs text-gray-500">{description}</p>
</div>
<button
onClick={() => setIsExpanded(false)}
className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-gray-400 hover:text-white rounded-lg transition-all text-sm font-medium"
>
Close
</button>
</div>
{/* Two-Panel Layout */}
<div className="grid grid-cols-2 gap-6 p-6 flex-1 overflow-hidden">
{/* Left: Code */}
<div className="flex flex-col h-full min-h-0">
<div className="flex items-center gap-2 mb-4 flex-wrap min-h-[32px] flex-shrink-0">
{(['curl', 'javascript', 'python', 'go', 'js-sdk', 'mcp'] as Language[]).map((lang) => (
<button
key={lang}
onClick={() => setLanguage(lang)}
className={`px-3 py-1.5 text-xs rounded-lg font-medium transition-all border ${
language === lang
? 'bg-slate-700 text-white border-slate-600'
: 'bg-slate-800/50 text-gray-400 border-slate-700/50 hover:border-slate-600 hover:text-gray-300'
}`}
>
{getLanguageLabel(lang)}
</button>
))}
</div>
<div className="border border-slate-700/50 rounded-xl overflow-hidden flex-1 flex flex-col min-h-0">
<div className="flex items-center justify-between bg-slate-950/80 px-4 py-3 border-b border-slate-700/50 flex-shrink-0">
<span className="text-xs text-gray-400 font-medium">Code Example</span>
<button
onClick={copyCode}
className="text-xs text-gray-400 hover:text-white transition-colors font-medium"
>
Copy
</button>
</div>
<div className="p-4 bg-slate-950/50 overflow-auto flex-1 min-h-0">
<pre className="text-sm text-gray-300 leading-relaxed">
<code>{generateCode()}</code>
</pre>
</div>
</div>
</div>
{/* Right: Response */}
<div className="flex flex-col h-full min-h-0">
<h4 className="text-sm font-semibold text-white mb-4 min-h-[32px] flex items-center flex-shrink-0">
Response
</h4>
<div className="border border-slate-700/50 rounded-xl overflow-hidden flex-1 flex flex-col min-h-0">
<div className="bg-slate-950/80 px-4 py-3 border-b border-slate-700/50 flex-shrink-0">
{response && (
<span
className={`inline-flex items-center gap-2 px-3 py-1 rounded text-xs font-bold ${
response.success
? 'bg-green-500/20 text-green-400 border border-green-500/40'
: 'bg-red-500/20 text-red-400 border border-red-500/40'
}`}
>
{response.success ? 'Success' : 'Error'}
</span>
)}
</div>
<div className="p-4 bg-slate-950/50 overflow-auto flex-1 min-h-0">
{error ? (
<div className="p-4 bg-red-500/10 border border-red-500/40 rounded-xl text-red-400 text-sm">
<p className="font-semibold mb-1">Error</p>
<p className="text-xs">{error}</p>
</div>
) : response ? (
<pre className="text-sm text-gray-300 leading-relaxed">
<code>{JSON.stringify(response, null, 2)}</code>
</pre>
) : (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<p className="text-sm text-gray-500">
Click <strong className="text-white">"Try It"</strong> below to see the response
</p>
</div>
</div>
)}
</div>
</div>
{/* Try It Button in Expanded View */}
<button
onClick={executeRequest}
disabled={!apiKey || isExecuting}
className="mt-4 px-4 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 text-white text-sm font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0"
>
{isExecuting ? 'Executing...' : 'Try It'}
</button>
</div>
</div>
</div>
</div>
)}
</>
);
};

View File

@ -0,0 +1,281 @@
'use client';
/**
* LinkCard Component
*
* An interactive navigation card for guiding users to related documentation pages.
* Uses color accents and hover effects to create an engaging, explorable experience.
*
* ## Design Principles
*
* 1. **Visual Affordance**: Design signals interactivity
* - Gradient backgrounds hint at clickability
* - Hover states provide immediate feedback
* - Cursor changes to pointer on hover
* - Border transitions guide attention
*
* 2. **Semantic Color System**: Accent colors convey meaning
* - Primary (Purple): Main content, API references
* - Secondary (Cyan): Supporting content, guides
* - Success (Green): Getting started, tutorials
* - Warning (Amber): Important notices, deprecations
*
* 3. **Information Hierarchy**: Title dominates, description supports
* - Large, bold title draws attention
* - Smaller, muted description provides context
* - Title color changes on hover for feedback
* - Clean vertical rhythm (title description)
*
* ## Accent Color Semantics
*
* @param {'primary'} primary - Main navigational elements, API documentation
* - Visual: Purple gradient and border
* - Psychology: Primary brand, core content, important
* - Use cases: "API Reference", "Core Concepts", main docs
* - Gradient: from-purple-500/10
* - Hover: border-purple-500/40, text-purple-400
*
* @param {'secondary'} secondary - Supporting content, guides, tutorials
* - Visual: Cyan gradient and border
* - Psychology: Complementary, helpful, educational
* - Use cases: "Guides", "Tutorials", "Best Practices"
* - Gradient: from-cyan-500/10
* - Hover: border-cyan-500/40, text-cyan-400
*
* @param {'success'} success - Getting started, quick wins, positive actions
* - Visual: Green gradient and border
* - Psychology: Achievement, beginning, growth
* - Use cases: "Getting Started", "Quick Start", "First Steps"
* - Gradient: from-green-500/10
* - Hover: border-green-500/40, text-green-400
*
* @param {'warning'} warning - Important notices, cautions, deprecations
* - Visual: Amber gradient and border
* - Psychology: Attention, caution, important
* - Use cases: "Migration Guide", "Breaking Changes", "Deprecations"
* - Gradient: from-amber-500/10
* - Hover: border-amber-500/40, text-amber-400
*
* ## Interactive States
*
* Default State:
* - Subtle gradient background (from-{color}-500/10)
* - Muted border (border-slate-700/50)
* - White title, gray description
* - Inviting but not demanding attention
*
* Hover State:
* - Border brightens to accent color (/40 opacity)
* - Title transitions to accent color
* - Smooth transition (transition-colors)
* - Clear feedback that element is clickable
*
* ## Layout Structure
*
* Padding System:
* - p-5 (20px): Generous padding for comfortable clicking
* - Creates substantial hit area for accessibility
* - Provides breathing room for content
*
* Border & Radius:
* - rounded-xl (12px): Modern, friendly appearance
* - border: Defined structure
* - Matches other card components in system
*
* ## Typography System
*
* Title (h3):
* - text-lg (18px): Large enough to be primary visual element
* - font-semibold (600): Strong but not overbearing
* - text-white: Maximum contrast and readability
* - mb-2 (8px): Separates from description
* - Transitions to accent color on hover
*
* Description (p):
* - text-sm (14px): Clearly secondary to title
* - text-gray-400: Muted, supporting role
* - Provides context without competing with title
*
* ## Accessibility Features
*
* - Semantic HTML (<a> tag for links)
* - Proper heading hierarchy (h3 for card titles)
* - Keyboard accessible (focusable link)
* - Sufficient color contrast in all states
* - Large click target (full card clickable)
* - Clear focus states (browser default + hover)
* - Descriptive link text (title + description)
*
* ## Usage Examples
*
* ```tsx
* // Primary content link
* <LinkCard
* href="/docs/api/text-to-image"
* title="API Reference"
* description="Explore all available endpoints, parameters, and response formats."
* accent="primary"
* />
*
* // Getting started
* <LinkCard
* href="/docs/quick-start"
* title="Quick Start"
* description="Get up and running with Banatie API in 5 minutes."
* accent="success"
* />
*
* // Supporting guide
* <LinkCard
* href="/docs/guides/authentication"
* title="Authentication Guide"
* description="Learn about API keys, rate limits, and security best practices."
* accent="secondary"
* />
*
* // Important notice
* <LinkCard
* href="/docs/migration"
* title="Migration Guide"
* description="Upgrade to v2 API with this step-by-step migration guide."
* accent="warning"
* />
* ```
*
* ## Content Guidelines
*
* Title:
* - 2-5 words ideal
* - Action-oriented or noun-based
* - Clear and specific
* - Sentence case
* - Examples: "API Reference", "Quick Start", "Authentication Guide"
*
* Description:
* - One sentence (10-20 words)
* - Explain what user will find/learn
* - Start with verb (Explore, Learn, Discover)
* - End with period
* - Be specific about content
*
* Accent Color:
* - Choose based on content type and importance
* - Maintain consistency across similar pages
* - Use primary for most API/core documentation
* - Reserve warning for truly important notices
*
* ## Visual Language
*
* The LinkCard system creates:
* - Intuitive navigation patterns
* - Clear content categorization through color
* - Engaging, explorable documentation
* - Professional yet friendly appearance
* - Consistent user experience across all docs
*
* @component
* @example
* <LinkCard
* href="/docs/api"
* title="API Reference"
* description="Complete API documentation"
* accent="primary"
* />
*/
/**
* Accent color determining the card's visual theme
*/
export type LinkCardAccent = 'primary' | 'secondary' | 'success' | 'warning';
/**
* Props for the LinkCard component
*/
export interface LinkCardProps {
/** Navigation URL */
href: string;
/** Card title (renders as h3) */
title: string;
/** Supporting description */
description: string;
/** Visual accent color */
accent: LinkCardAccent;
/** Optional icon or emoji to display */
icon?: string;
/** Optional CSS class name for additional styling */
className?: string;
}
/**
* Accent styles mapping color to gradient and hover colors
*/
const accentStyles: Record<LinkCardAccent, {
gradient: string;
hoverBorder: string;
hoverText: string;
}> = {
primary: {
gradient: 'from-purple-500/10',
hoverBorder: 'hover:border-purple-500/40',
hoverText: 'group-hover:text-purple-400',
},
secondary: {
gradient: 'from-cyan-500/10',
hoverBorder: 'hover:border-cyan-500/40',
hoverText: 'group-hover:text-cyan-400',
},
success: {
gradient: 'from-green-500/10',
hoverBorder: 'hover:border-green-500/40',
hoverText: 'group-hover:text-green-400',
},
warning: {
gradient: 'from-amber-500/10',
hoverBorder: 'hover:border-amber-500/40',
hoverText: 'group-hover:text-amber-400',
},
};
export const LinkCard = ({
href,
title,
description,
accent,
icon,
className = '',
}: LinkCardProps) => {
const styles = accentStyles[accent];
return (
<a
href={href}
className={`
block
p-5
bg-gradient-to-br ${styles.gradient} to-transparent
border border-slate-700/50
rounded-xl
${styles.hoverBorder}
transition-colors
group
${className}
`}
>
{icon && (
<div className="text-2xl mb-3">{icon}</div>
)}
<h3
className={`
text-lg font-semibold text-white mb-2
${styles.hoverText}
transition-colors
`}
>
{title}
</h3>
<p className="text-sm text-gray-400">
{description}
</p>
</a>
);
};

View File

@ -0,0 +1,216 @@
'use client';
/**
* LinkCardGrid Component
*
* A responsive grid layout container for organizing multiple LinkCard components.
* Provides consistent spacing and responsive behavior for navigation card collections.
*
* ## Design Principles
*
* 1. **Responsive Layout**: Adapts gracefully to all screen sizes
* - Mobile: Single column for comfortable tap targets
* - Tablet/Desktop: Multi-column for efficient space usage
* - Fluid transitions between breakpoints
*
* 2. **Grid System**: CSS Grid provides flexible, robust layout
* - Equal-width columns maintain balance
* - Auto-fit for flexible number of cards
* - Consistent gaps create visual rhythm
*
* 3. **Composition Pattern**: Wrapper abstracts layout complexity
* - Parent handles grid logic
* - Children (LinkCards) focus on content
* - Separation of concerns improves maintainability
*
* ## Column Configuration
*
* @param {2} columns=2 - Two-column layout (default)
* - Visual: Balanced, side-by-side comparison
* - Use cases: "Next Steps", paired content, binary choices
* - Responsive: md:grid-cols-2 (2 cols 768px, 1 col <768px)
* - Psychology: Compare and choose pattern
* - Example: "API Reference" vs "Authentication Guide"
*
* @param {3} columns=3 - Three-column layout
* - Visual: Information-rich, multiple options
* - Use cases: Resource hubs, category pages, multiple paths
* - Responsive: md:grid-cols-3 (3 cols 768px, 1 col <768px)
* - Psychology: Multiple choice, exploration
* - Example: "Getting Started", "API Docs", "Examples"
*
* @param {4} columns=4 - Four-column layout
* - Visual: Dense, comprehensive navigation
* - Use cases: Resource libraries, extensive catalogs
* - Responsive: md:grid-cols-2 lg:grid-cols-4 (2 cols tablet, 4 cols desktop)
* - Psychology: Broad exploration, many options
* - Example: Multiple API endpoints, extensive guides
*
* ## Responsive Breakpoints
*
* Mobile (<768px):
* - All grids collapse to single column
* - Prevents cramped cards on small screens
* - Optimizes for thumb-friendly tap targets
* - Maintains readability and usability
*
* Tablet (768px, <1024px):
* - 2-column: Displays as 2 columns
* - 3-column: Displays as 3 columns
* - 4-column: Displays as 2 columns (intermediate step)
* - Balances space usage and readability
*
* Desktop (1024px):
* - All grids display at full column count
* - Efficient use of available width
* - Supports quick visual scanning
*
* ## Spacing System
*
* Gap:
* - gap-4 (16px): Comfortable separation between cards
* - Creates visual grouping without crowding
* - Maintains clean grid appearance
* - Consistent with other spacing in design system
*
* Why gap-4?
* - Larger than text line-height (prevents cramping)
* - Small enough to show relationship between cards
* - Works well with card padding (p-5)
* - Scales gracefully at all screen sizes
*
* ## Grid Behavior
*
* CSS Grid Properties:
* - `grid`: Establishes grid container
* - `grid-cols-1`: Mobile baseline (single column)
* - `md:grid-cols-{n}`: Tablet/desktop columns
* - `gap-4`: Gutter between grid items
*
* ## Accessibility Considerations
*
* - Maintains logical reading order (top-to-bottom, left-to-right)
* - Grid doesn't interfere with keyboard navigation
* - Responsive design ensures usability on all devices
* - No JavaScript required (pure CSS grid)
* - Works with screen readers (semantic HTML preserved)
*
* ## Usage Examples
*
* ```tsx
* // Two-column "Next Steps" section
* <LinkCardGrid columns={2}>
* <LinkCard
* href="/docs/api"
* title="API Reference"
* description="Explore all endpoints"
* accent="primary"
* />
* <LinkCard
* href="/docs/guides"
* title="Guides"
* description="Learn best practices"
* accent="secondary"
* />
* </LinkCardGrid>
*
* // Three-column resource hub
* <LinkCardGrid columns={3}>
* <LinkCard href="..." title="Getting Started" accent="success" description="..." />
* <LinkCard href="..." title="API Docs" accent="primary" description="..." />
* <LinkCard href="..." title="Examples" accent="secondary" description="..." />
* </LinkCardGrid>
*
* // Four-column comprehensive navigation
* <LinkCardGrid columns={4}>
* <LinkCard href="..." title="Text to Image" accent="primary" description="..." />
* <LinkCard href="..." title="Upload" accent="primary" description="..." />
* <LinkCard href="..." title="Authentication" accent="secondary" description="..." />
* <LinkCard href="..." title="Rate Limits" accent="secondary" description="..." />
* </LinkCardGrid>
* ```
*
* ## Composition Guidelines
*
* DO:
* - Use consistent accent colors within a grid (e.g., all primary, or primary + secondary)
* - Keep card count balanced (even numbers work best for 2/4 columns)
* - Provide descriptive, parallel content in cards
* - Order cards by importance or logical flow
*
* DON'T:
* - Mix too many accent colors (creates visual chaos)
* - Use unbalanced card counts in 2-column (odd numbers leave gaps)
* - Nest grids inside grids (causes layout issues)
* - Put non-LinkCard components inside (breaks grid consistency)
*
* ## Visual Language
*
* The LinkCardGrid creates:
* - Organized, scannable navigation patterns
* - Professional documentation architecture
* - Clear visual hierarchy through layout
* - Responsive, mobile-friendly experience
* - Consistent spacing and alignment
*
* ## Performance
*
* - Pure CSS (no JavaScript overhead)
* - Minimal DOM elements (single wrapper div)
* - Hardware-accelerated grid layout
* - No re-renders or layout thrashing
*
* @component
* @example
* <LinkCardGrid columns={2}>
* {children}
* </LinkCardGrid>
*/
import { ReactNode } from 'react';
/**
* Column configuration for the grid
*/
export type GridColumns = 2 | 3 | 4;
/**
* Props for the LinkCardGrid component
*/
export interface LinkCardGridProps {
/** Number of columns at tablet/desktop breakpoints */
columns?: GridColumns;
/** LinkCard components to display in the grid */
children: ReactNode;
/** Optional CSS class name for additional styling */
className?: string;
}
/**
* Responsive grid class mappings for different column counts
*/
const columnStyles: Record<GridColumns, string> = {
2: 'md:grid-cols-2',
3: 'md:grid-cols-3',
4: 'md:grid-cols-2 lg:grid-cols-4', // 2 cols on tablet, 4 on desktop
};
export const LinkCardGrid = ({
columns = 2,
children,
className = '',
}: LinkCardGridProps) => {
return (
<div
className={`
grid
grid-cols-1
${columnStyles[columns]}
gap-4
${className}
`}
>
{children}
</div>
);
};

View File

@ -0,0 +1,181 @@
'use client';
/**
* MethodBadge Component
*
* A specialized badge for HTTP method display in API documentation.
* Uses industry-standard color conventions to communicate REST operation types.
*
* ## Design Principles
*
* 1. **REST Semantic Mapping**: Colors align with HTTP method semantics
* - Follows industry conventions (GitHub, Stripe, Swagger UI)
* - Immediate visual recognition for developers
* - Consistent with HTTP specification meanings
*
* 2. **Visual Distinction**: Each method has unique, memorable color
* - High contrast for readability against dark backgrounds
* - Distinct enough to differentiate at a glance
* - Professional appearance suitable for technical documentation
*
* 3. **Typography**: Bold, uppercase display reinforces technical nature
* - font-bold for emphasis and readability
* - Uppercase matches HTTP specification format
* - Monospace-friendly sizing
*
* ## HTTP Method Semantics
*
* @param {'GET'} GET - Read/Retrieve operations (Safe, Idempotent)
* - Visual: Cyan/Blue - represents data flow inward (reading)
* - Psychology: Information retrieval, safe operation
* - Use cases: Fetch resources, list endpoints, query data
* - HTTP: Safe method, no side effects, cacheable
*
* @param {'POST'} POST - Create operations (Not safe, Not idempotent)
* - Visual: Green - represents creation, addition, growth
* - Psychology: Positive action, new resource creation
* - Use cases: Create resource, submit form, trigger action
* - HTTP: Creates new resource, may have side effects
*
* @param {'PUT'} PUT - Update/Replace operations (Not safe, Idempotent)
* - Visual: Amber/Orange - represents modification, change
* - Psychology: Caution, transformation, state change
* - Use cases: Update entire resource, replace content
* - HTTP: Idempotent, full resource update
*
* @param {'PATCH'} PATCH - Partial Update operations (Not safe, Not idempotent)
* - Visual: Purple - represents refinement, partial change
* - Psychology: Precision, targeted modification
* - Use cases: Update specific fields, partial resource modification
* - HTTP: Partial update, may not be idempotent
*
* @param {'DELETE'} DELETE - Delete operations (Not safe, Idempotent)
* - Visual: Red - represents removal, destruction, danger
* - Psychology: Warning, irreversible action, stop
* - Use cases: Remove resource, deactivate, destroy data
* - HTTP: Idempotent, removes resource
*
* ## Size Variants
*
* @param {'sm'} sm - Small size for inline usage (px-2 py-0.5, text-xs)
* @param {'md'} md - Medium size for standard usage (px-3 py-1, text-xs) [default]
*
* ## Visual Language
*
* - **Rounded corners**: Modern, friendly appearance
* - **Semi-transparent backgrounds**: Integrates with dark theme
* - **Solid borders**: Adds definition and structure
* - **Bold text**: Ensures readability and emphasis
* - **Consistent sizing**: Aligns with other badges in the design system
*
* Color Opacity Strategy:
* - Background: 20% opacity for subtle presence
* - Border: 40% opacity for clear definition
* - Text: 100% (400 weight) for maximum readability
*
* ## Usage Examples
*
* ```tsx
* // API Endpoint Documentation
* <MethodBadge method="GET" /> /api/users
* <MethodBadge method="POST" /> /api/users
*
* // With size variants
* <MethodBadge method="DELETE" size="sm" />
* <MethodBadge method="PUT" size="md" />
*
* // In endpoint cards
* <div className="flex items-center gap-2">
* <MethodBadge method="POST" />
* <code>/api/text-to-image</code>
* </div>
* ```
*
* ## Industry Standards Reference
*
* This color scheme aligns with popular API documentation tools:
* - Swagger UI: Blue (GET), Green (POST), Orange (PUT), Red (DELETE)
* - Postman: Similar color conventions
* - GitHub API Docs: Consistent method color coding
*
* @component
* @example
* <MethodBadge method="POST" size="md" />
*/
import { ReactNode } from 'react';
/**
* HTTP method types following REST conventions
*/
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
/**
* Size variant for the badge
*/
export type MethodBadgeSize = 'sm' | 'md';
/**
* Props for the MethodBadge component
*/
export interface MethodBadgeProps {
/** The HTTP method to display */
method: HttpMethod;
/** Size variant (default: 'md') */
size?: MethodBadgeSize;
/** Optional CSS class name for additional styling */
className?: string;
}
/**
* Color system mapping HTTP methods to semantic colors
* Follows industry standards for API documentation
*/
const methodStyles: Record<HttpMethod, string> = {
GET: 'bg-cyan-600/20 text-cyan-400 border-cyan-500/40',
POST: 'bg-green-600/20 text-green-400 border-green-500/40',
PUT: 'bg-amber-600/20 text-amber-400 border-amber-500/40',
PATCH: 'bg-purple-600/20 text-purple-400 border-purple-500/40',
DELETE: 'bg-red-600/20 text-red-400 border-red-500/40',
};
/**
* Size system mapping size variant to padding and font size
*/
const sizeStyles: Record<MethodBadgeSize, string> = {
sm: 'px-2 py-0.5 text-xs',
md: 'px-3 py-1 text-xs',
};
/**
* Accessibility labels describing the HTTP method's purpose
*/
const methodLabels: Record<HttpMethod, string> = {
GET: 'GET request - retrieve data',
POST: 'POST request - create resource',
PUT: 'PUT request - update resource',
PATCH: 'PATCH request - partial update',
DELETE: 'DELETE request - remove resource',
};
export const MethodBadge = ({
method,
size = 'md',
className = '',
}: MethodBadgeProps) => {
return (
<span
className={`
inline-flex items-center justify-center
rounded border font-bold
${methodStyles[method]}
${sizeStyles[size]}
${className}
`}
role="status"
aria-label={methodLabels[method]}
>
{method}
</span>
);
};

View File

@ -0,0 +1,244 @@
'use client';
/**
* ResponseBlock Component
*
* A semantic display block for API response examples with status indicators.
* Visually differentiates success and error responses through color psychology.
*
* ## Design Principles
*
* 1. **Status Communication**: Color instantly conveys response type
* - Green = Success (2xx responses, positive outcomes)
* - Red = Error (4xx/5xx responses, failures)
* - Consistent with universal traffic light conventions
*
* 2. **Information Layering**: Progressive disclosure of details
* - Status badge in top-right for immediate recognition
* - Optional title provides context
* - Content area for actual response data
* - Layered styling adds depth without clutter
*
* 3. **Visual Feedback**: Subtle effects reinforce status
* - Background tint matches status color
* - Border emphasis adds structure
* - Soft shadow/glow creates depth
* - All at low opacity to avoid overwhelming
*
* ## Status Semantics
*
* @param {'success'} success - Successful API responses (2xx status codes)
* - Visual: Green color palette (green-500)
* - Psychology: Positive, correct, achieved
* - Use cases: 200 OK, 201 Created, 204 No Content
* - Styling: bg-green-500/5, border-green-500/30, shadow-green-500/10
* - Badge: "✓ 200 Success", "✓ 201 Created"
*
* @param {'error'} error - Error API responses (4xx/5xx status codes)
* - Visual: Red color palette (red-500)
* - Psychology: Warning, problem, attention needed
* - Use cases: 400 Bad Request, 401 Unauthorized, 500 Server Error
* - Styling: bg-red-500/5, border-red-500/30, shadow-red-500/10
* - Badge: "✗ 400 Error", "✗ 500 Error"
*
* ## Opacity Strategy
*
* Three-tier opacity system:
* - Background: /5 (5% opacity) - Subtle tint, doesn't obscure content
* - Border: /30 (30% opacity) - Clear definition, visible structure
* - Shadow: /10 (10% opacity) - Soft depth, enhances without distraction
*
* This creates:
* - Cohesive visual grouping
* - Clear status communication
* - Maintains content readability
* - Professional, polished appearance
*
* ## Layout Structure
*
* ```
*
* [STATUS BADGE] Absolute positioned
* Optional Title
* (if provided)
*
* <pre><code>
* Response content
* ...
* </code></pre>
*
* ```
*
* Positioning Strategy:
* - Container: relative positioning context
* - Badge: absolute top-3 right-3 (float in top-right)
* - Content: mt-6 when badge present (clear space for badge)
* - Title: mb-3 if provided (separate from content)
*
* ## Typography & Content
*
* Title (optional):
* - text-sm: Small, not competing with main content
* - font-semibold: Subtle emphasis
* - text-gray-300: Readable but secondary
* - Purpose: Labels response type, adds context
*
* Response Content:
* - text-xs: Appropriate for code samples
* - text-gray-300: High contrast on dark background
* - font-mono (implicit in <code>): Code appearance
* - overflow-x-auto: Handles long lines gracefully
*
* ## Usage Examples
*
* ```tsx
* // Success response with title
* <ResponseBlock
* status="success"
* statusCode={200}
* title="Expected Response:"
* content={`{
* "success": true,
* "data": { ... }
* }`}
* />
*
* // Error response without title
* <ResponseBlock
* status="error"
* statusCode={401}
* content={`{
* "error": "Unauthorized",
* "message": "Invalid API key"
* }`}
* />
*
* // Custom status label
* <ResponseBlock
* status="success"
* statusCode={201}
* statusLabel="✓ 201 Created"
* content="{ ... }"
* />
* ```
*
* ## Content Guidelines
*
* Response Content:
* - Use actual JSON/text from your API
* - Format with proper indentation (2 or 4 spaces)
* - Include realistic data (not just placeholders)
* - Show relevant fields (omit excessive detail)
* - Keep under 20-30 lines for readability
*
* Status Labels:
* - Include status code (200, 400, etc.)
* - Add descriptive text (Success, Error, Created)
* - Use checkmark () for success
* - Use X () for errors
* - Keep concise (2-3 words max)
*
* ## Accessibility
*
* - Semantic HTML structure (<pre><code>)
* - Color is supplementary (text + symbols)
* - High contrast text colors
* - StatusBadge includes aria-label
* - Proper heading hierarchy if title used
* - Scrollable overflow for long content
*
* ## Visual Language
*
* The ResponseBlock creates:
* - Immediate status recognition through color
* - Professional code presentation
* - Clear example/documentation relationship
* - Consistent pattern across all API docs
* - Supports learn-by-example documentation style
*
* @component
* @example
* <ResponseBlock
* status="success"
* statusCode={200}
* title="Success Response"
* content={jsonString}
* />
*/
import { StatusBadge, StatusType } from './StatusBadge';
/**
* Props for the ResponseBlock component
*/
export interface ResponseBlockProps {
/** Status type determining color scheme */
status: Extract<StatusType, 'success' | 'error'>;
/** HTTP status code (e.g., 200, 400, 500) */
statusCode: number;
/** The response content to display (typically JSON) */
content: string;
/** Optional title above the response */
title?: string;
/** Optional custom status label (default: auto-generated from statusCode) */
statusLabel?: string;
/** Optional CSS class name for additional styling */
className?: string;
}
/**
* Status styles mapping status type to background, border, and shadow colors
*/
const statusStyles = {
success: 'bg-green-500/5 border-green-500/30 shadow-lg shadow-green-500/10',
error: 'bg-red-500/5 border-red-500/30 shadow-lg shadow-red-500/10',
};
/**
* Generate default status label from code and status
*/
const getDefaultStatusLabel = (code: number, status: 'success' | 'error'): string => {
const symbol = status === 'success' ? '✓' : '✗';
const text = status === 'success' ? 'Success' : 'Error';
return `${symbol} ${code} ${text}`;
};
export const ResponseBlock = ({
status,
statusCode,
content,
title,
statusLabel,
className = '',
}: ResponseBlockProps) => {
const label = statusLabel || getDefaultStatusLabel(statusCode, status);
return (
<div
className={`
relative
p-4
border rounded-xl
${statusStyles[status]}
${className}
`}
>
{/* Status Badge - Floating Top Right */}
<div className="absolute top-3 right-3">
<StatusBadge status={status} size="sm">
{label}
</StatusBadge>
</div>
{/* Optional Title */}
{title && (
<p className="text-sm font-semibold text-gray-300 mb-3">{title}</p>
)}
{/* Response Content */}
<pre className={`text-xs text-gray-300 overflow-x-auto ${title || statusLabel ? 'mt-6' : ''}`}>
<code>{content}</code>
</pre>
</div>
);
};

View File

@ -0,0 +1,199 @@
'use client';
/**
* SectionHeader Component
*
* Consistent heading elements for organizing documentation content into hierarchical sections.
* Provides visual rhythm and structure throughout documentation pages.
*
* ## Design Principles
*
* 1. **Visual Hierarchy**: Size and weight indicate content importance
* - Level 2 (h2): Major sections, primary content divisions
* - Level 3 (h3): Subsections, supporting details
* - Consistent styling creates predictable structure
*
* 2. **Semantic HTML**: Proper heading levels for accessibility
* - Uses correct HTML heading tags (h2, h3)
* - Supports screen readers and document outline
* - Enables Table of Contents generation
* - Critical for SEO and document structure
*
* 3. **Spacing System**: Establishes vertical rhythm
* - Top margin separates from previous content
* - Bottom margin creates breathing room before content
* - Consistent spacing improves scannability
*
* ## Heading Level Semantics
*
* @param {2} level2 - Major section headings (h2)
* - Visual: text-3xl (30px), font-bold, text-white
* - Spacing: mb-4 (16px) below, mb-6 (24px) for sections with mb-12
* - Psychology: Primary divisions, main topics
* - Use cases: "Overview", "Parameters", "Response", "Error Codes"
* - Hierarchy: Second level (after page h1/Hero)
* - SEO: Key topic signals for search engines
*
* @param {3} level3 - Subsection headings (h3)
* - Visual: text-xl (20px), font-semibold, text-white
* - Spacing: mb-3 (12px) below, mb-4 (16px) for subsections with mb-8
* - Psychology: Supporting topics, detailed breakdowns
* - Use cases: "Key Types", "Creating Keys", "Rate Limits"
* - Hierarchy: Third level (under h2 sections)
* - SEO: Supporting detail signals
*
* ## Typography Scale Reasoning
*
* Level 2 (text-3xl / 30px):
* - Large enough to clearly mark major sections
* - Smaller than Hero (36-48px) to maintain hierarchy
* - Bold weight adds emphasis and structure
* - White color ensures readability and prominence
*
* Level 3 (text-xl / 20px):
* - Noticeably smaller than h2, maintaining hierarchy
* - Semibold (600) instead of bold provides subtle distinction
* - Large enough to serve as clear subsection marker
* - Balances prominence with subordinate role
*
* ## Spacing Philosophy
*
* Bottom Margin Strategy:
* - h2: mb-4 or mb-6 depending on section context
* - mb-4 for tighter sections with nearby content
* - mb-6 for major sections introducing substantial content
* - h3: mb-3 or mb-4 depending on subsection context
* - Smaller margins reflect subordinate hierarchy
* - Still provides clear separation from content
*
* ## Anchor Link Support
*
* The `id` prop enables:
* - Direct linking to specific sections (#section-name)
* - Table of Contents navigation
* - Deep linking from external sources
* - Browser history for scrolled positions
*
* ID Naming Convention:
* - Use kebab-case (lowercase with hyphens)
* - Descriptive and unique per page
* - Example: "api-keys", "rate-limits", "error-handling"
*
* ## Accessibility Features
*
* - Semantic HTML heading levels (h2, h3)
* - Proper document outline structure
* - Sufficient color contrast (white on dark)
* - Clear visual distinction between levels
* - Screen reader friendly navigation
* - Keyboard accessible anchor links
*
* ## Usage Examples
*
* ```tsx
* // Major section heading (h2)
* <SectionHeader level={2} id="overview">
* Overview
* </SectionHeader>
*
* // Subsection heading (h3)
* <SectionHeader level={3} id="key-types">
* Key Types
* </SectionHeader>
*
* // Custom spacing with className
* <SectionHeader level={2} id="parameters" className="mb-6">
* Parameters
* </SectionHeader>
* ```
*
* ## Content Guidelines
*
* Level 2 Headings:
* - 1-3 words ideal (concise and scannable)
* - Sentence case (capitalize first word)
* - Action-oriented or descriptive nouns
* - Examples: "Quick Start", "Authentication", "Rate Limits"
*
* Level 3 Headings:
* - 1-4 words, slightly more descriptive
* - Sentence case
* - Specific subtopics or steps
* - Examples: "Creating Keys", "Using API Keys", "Security Best Practices"
*
* ## Visual Language
*
* The header system creates a visual rhythm that:
* - Guides the eye through content naturally
* - Breaks long pages into digestible chunks
* - Signals importance through size and weight
* - Maintains consistency across all documentation
* - Supports quick scanning and navigation
*
* @component
* @example
* <SectionHeader level={2} id="getting-started">
* Getting Started
* </SectionHeader>
*/
import { ReactNode } from 'react';
/**
* Heading level determining HTML tag and visual styling
*/
export type SectionHeaderLevel = 2 | 3;
/**
* Props for the SectionHeader component
*/
export interface SectionHeaderProps {
/** Heading level (h2 or h3) for semantic structure */
level: SectionHeaderLevel;
/** The heading text content */
children: ReactNode;
/** Optional ID for anchor linking and table of contents */
id?: string;
/** Optional CSS class name for additional styling (e.g., custom margins) */
className?: string;
}
/**
* Typography styles mapping heading level to text size and weight
*/
const levelStyles: Record<SectionHeaderLevel, string> = {
2: 'text-3xl font-bold',
3: 'text-xl font-semibold',
};
/**
* Default spacing system for bottom margins
*/
const defaultSpacing: Record<SectionHeaderLevel, string> = {
2: 'mb-4',
3: 'mb-3',
};
export const SectionHeader = ({
level,
children,
id,
className,
}: SectionHeaderProps) => {
const Tag = `h${level}` as 'h2' | 'h3';
const spacing = className?.includes('mb-') ? '' : defaultSpacing[level];
return (
<Tag
id={id}
className={`
${levelStyles[level]}
text-white
${spacing}
${className || ''}
`}
>
{children}
</Tag>
);
};

View File

@ -0,0 +1,176 @@
'use client';
/**
* StatusBadge Component
*
* A semantic status indicator that uses color psychology and consistent styling
* to communicate state, result, or severity across documentation.
*
* ## Design Principles
*
* 1. **Semantic Color System**: Colors convey meaning, not just decoration
* - Uses consistent color mapping across all documentation
* - Follows industry standards (green = success, red = error, etc.)
*
* 2. **Visual Hierarchy**: Small, unobtrusive elements that enhance readability
* - Pill-shaped design for friendly, modern appearance
* - Subtle backgrounds with semi-transparent colors
* - Border emphasis for definition without heaviness
*
* 3. **Accessibility**: Meets WCAG 2.1 AA standards
* - Sufficient color contrast ratios
* - Semantic HTML with aria-label
* - Text labels (never color-only communication)
*
* ## Semantic Status Values
*
* @param {'success'} success - Positive outcomes, completed actions, 2xx HTTP responses
* - Visual: Green color palette
* - Psychology: Achievement, correctness, go-ahead signal
* - Use cases: "200 Success", "Completed", "Active", "✓ Passed"
*
* @param {'info'} info - Neutral information, documentation, helpful tips
* - Visual: Blue/Cyan color palette
* - Psychology: Calm, trustworthy, educational
* - Use cases: "Beta", "New", "Info", "Note"
*
* @param {'warning'} warning - Caution, deprecation, rate limits, 4xx client errors
* - Visual: Amber/Orange color palette
* - Psychology: Attention needed, proceed with caution
* - Use cases: "429 Rate Limit", "Deprecated", "Warning", "Limited"
*
* @param {'error'} error - Failures, critical issues, 5xx server errors, security alerts
* - Visual: Red color palette
* - Psychology: Stop, danger, requires immediate attention
* - Use cases: "500 Error", "Failed", "Critical", "Blocked"
*
* @param {'neutral'} neutral - Default state, placeholder, unspecified
* - Visual: Slate/Gray color palette
* - Psychology: Neutral, balanced, inactive
* - Use cases: "Pending", "Unknown", "Draft", "Inactive"
*
* ## Size Variants
*
* @param {'sm'} sm - Small size for inline usage, tight spaces
* - Font: text-xs
* - Padding: px-2 py-0.5
* - Use: Inline with text, table cells, compact layouts
*
* @param {'md'} md - Medium size (default) for standard usage
* - Font: text-xs
* - Padding: px-3 py-1
* - Use: General purpose, code examples, section markers
*
* ## Visual Language
*
* The badge uses a rounded-full design (pill shape) which:
* - Creates visual consistency with other UI elements (buttons, tags)
* - Feels modern and friendly (vs. harsh rectangular badges)
* - Groups related information visually
* - Works well at small sizes without appearing cramped
*
* Color opacity levels (X/20 bg, X/40 border):
* - Background: 20% opacity for subtle presence
* - Border: 40% opacity for definition
* - Text: 100% for maximum readability
*
* ## Usage Examples
*
* ```tsx
* // HTTP Status Codes
* <StatusBadge status="success" size="sm">200 OK</StatusBadge>
* <StatusBadge status="error" size="sm">500 Error</StatusBadge>
*
* // API States
* <StatusBadge status="info">Beta</StatusBadge>
* <StatusBadge status="warning">Deprecated</StatusBadge>
*
* // System Status
* <StatusBadge status="success"> Active</StatusBadge>
* <StatusBadge status="neutral">Pending</StatusBadge>
* ```
*
* @component
* @example
* <StatusBadge status="success" size="md">200 Success</StatusBadge>
*/
import { ReactNode } from 'react';
/**
* Semantic status type representing the meaning/severity of the badge
*/
export type StatusType = 'success' | 'info' | 'warning' | 'error' | 'neutral';
/**
* Size variant for the badge
*/
export type StatusSize = 'sm' | 'md';
/**
* Props for the StatusBadge component
*/
export interface StatusBadgeProps {
/** The semantic status that determines color and meaning */
status: StatusType;
/** The content to display inside the badge */
children: ReactNode;
/** Size variant (default: 'md') */
size?: StatusSize;
/** Optional CSS class name for additional styling */
className?: string;
}
/**
* Color system mapping semantic status to Tailwind color utilities
* Follows the established design system color palette
*/
const statusStyles: Record<StatusType, string> = {
success: 'bg-green-500/20 text-green-400 border border-green-500/40',
info: 'bg-cyan-500/20 text-cyan-400 border border-cyan-500/40',
warning: 'bg-amber-500/20 text-amber-400 border border-amber-500/40',
error: 'bg-red-500/20 text-red-400 border border-red-500/40',
neutral: 'bg-slate-500/20 text-slate-400 border border-slate-500/40',
};
/**
* Size system mapping size variant to padding and font size
*/
const sizeStyles: Record<StatusSize, string> = {
sm: 'px-2 py-0.5 text-xs',
md: 'px-3 py-1 text-xs',
};
/**
* Human-readable labels for screen readers
*/
const statusLabels: Record<StatusType, string> = {
success: 'Success status',
info: 'Information status',
warning: 'Warning status',
error: 'Error status',
neutral: 'Neutral status',
};
export const StatusBadge = ({
status,
children,
size = 'md',
className = '',
}: StatusBadgeProps) => {
return (
<span
className={`
inline-flex items-center justify-center
rounded-full font-bold
${statusStyles[status]}
${sizeStyles[size]}
${className}
`}
role="status"
aria-label={statusLabels[status]}
>
{children}
</span>
);
};

View File

@ -0,0 +1,176 @@
/**
* Documentation Block Components
*
* A comprehensive library of reusable, semantic components for building
* consistent, professional API documentation.
*
* ## Design System Overview
*
* This collection establishes a visual language for documentation through:
* - **Semantic Color System**: Enum-based color values with clear meanings
* - **Consistent Typography**: Hierarchical text sizing and weights
* - **Responsive Layouts**: Mobile-first, adaptive components
* - **Accessibility First**: WCAG 2.1 AA compliance throughout
* - **Comprehensive Documentation**: Every component fully documented
*
* ## Component Categories
*
* ### Status & Indicators
* - **StatusBadge**: Semantic status indicators (success, info, warning, error, neutral)
* - **MethodBadge**: HTTP method badges (GET, POST, PUT, PATCH, DELETE)
*
* ### Typography & Content
* - **Hero**: Page-level headings with title and subtitle
* - **SectionHeader**: Hierarchical section headings (h2, h3)
* - **InlineCode**: Semantic inline code elements
*
* ### API Documentation
* - **EndpointCard**: Structured API endpoint display
* - **ResponseBlock**: API response examples with status visualization
*
* ### Navigation
* - **LinkCard**: Interactive navigation cards with accent colors
* - **LinkCardGrid**: Responsive grid layout for link cards
*
* ## Usage Philosophy
*
* These components follow key principles:
*
* 1. **Composition over Configuration**
* - Small, focused components combine to create pages
* - Each component does one thing well
* - Easy to understand and maintain
*
* 2. **Semantic Props**
* - Enum values over raw values (accent='primary' not color='#8b5cf6')
* - Meaning-based names (status='success' not color='green')
* - Self-documenting API
*
* 3. **Consistent Patterns**
* - Similar props across components (className, children)
* - Predictable behavior and styling
* - Easy to learn and use
*
* 4. **Accessibility Built-in**
* - Semantic HTML throughout
* - ARIA labels where appropriate
* - Keyboard navigation support
* - Sufficient color contrast
*
* ## Example: Building a Documentation Page
*
* ```tsx
* import {
* Hero,
* SectionHeader,
* InlineCode,
* EndpointCard,
* ResponseBlock,
* LinkCard,
* LinkCardGrid,
* } from '@/components/docs/blocks';
*
* export default function APIPage() {
* return (
* <>
* <Hero
* title="Text to Image API"
* subtitle="Generate images from text prompts"
* />
*
* <SectionHeader level={2} id="endpoint">
* Endpoint
* </SectionHeader>
*
* <EndpointCard
* method="POST"
* endpoint="/api/text-to-image"
* />
*
* <SectionHeader level={2} id="response">
* Response
* </SectionHeader>
*
* <ResponseBlock
* status="success"
* statusCode={200}
* content={responseJSON}
* />
*
* <SectionHeader level={2} id="next-steps">
* Next Steps
* </SectionHeader>
*
* <LinkCardGrid columns={2}>
* <LinkCard
* href="/docs/guides"
* title="Guides"
* description="Learn best practices"
* accent="secondary"
* />
* <LinkCard
* href="/docs/examples"
* title="Examples"
* description="See code samples"
* accent="primary"
* />
* </LinkCardGrid>
* </>
* );
* }
* ```
*
* ## Color System Reference
*
* ### Semantic Colors
* - **primary**: Purple - Brand primary, main content
* - **secondary**: Cyan - Complementary, supporting content
* - **success**: Green - Positive outcomes, achievements
* - **info**: Blue/Cyan - Informational, neutral
* - **warning**: Amber - Caution, important notices
* - **error**: Red - Errors, problems, danger
* - **neutral**: Slate/Gray - Default, inactive
*
* ### HTTP Method Colors
* - **GET**: Cyan - Read operations
* - **POST**: Green - Create operations
* - **PUT**: Amber - Update operations
* - **PATCH**: Purple - Partial update
* - **DELETE**: Red - Delete operations
*
* ## Spacing System
*
* Consistent spacing creates visual rhythm:
* - **sm**: Tight spacing for compact layouts
* - **md**: Standard spacing (default for most components)
* - **lg**: Generous spacing for major sections
*
* ## Typography Scale
*
* Hierarchical text sizing:
* - **Hero**: text-4xl text-6xl (large, page-level)
* - **H2**: text-3xl (section headings)
* - **H3**: text-xl (subsection headings)
* - **Body**: text-base (paragraph text)
* - **Small**: text-sm (supporting text)
* - **Code**: text-xs (code samples)
*
* @module blocks
*/
// Status & Indicators
export { StatusBadge, type StatusBadgeProps, type StatusType, type StatusSize } from './StatusBadge';
export { MethodBadge, type MethodBadgeProps, type HttpMethod, type MethodBadgeSize } from './MethodBadge';
// Typography & Content
export { Hero, type HeroProps, type HeroSize } from './Hero';
export { SectionHeader, type SectionHeaderProps, type SectionHeaderLevel } from './SectionHeader';
export { InlineCode, type InlineCodeProps, type InlineCodeColor } from './InlineCode';
// API Documentation
export { EndpointCard, type EndpointCardProps } from './EndpointCard';
export { ResponseBlock, type ResponseBlockProps } from './ResponseBlock';
// Navigation
export { LinkCard, type LinkCardProps, type LinkCardAccent } from './LinkCard';
export { LinkCardGrid, type LinkCardGridProps, type GridColumns } from './LinkCardGrid';

View File

@ -0,0 +1,142 @@
'use client';
/**
* Documentation Page Layout Component
*
* Provides consistent structure for all documentation pages:
* - Breadcrumb navigation at top
* - Article content area (passed as children)
* - Next Steps section at bottom
* - Table of Contents sidebar on the right
*
* Uses ThreeColumnLayout for consistent column structure:
* - Center: Article content (max-w-3xl)
* - Right: Table of Contents (w-56, hidden xl:block)
*
* This component extracts common layout patterns from all doc pages,
* reducing duplication and ensuring consistency.
*
* Usage:
* ```tsx
* <DocPage
* breadcrumbItems={[...]}
* tocItems={[...]}
* nextSteps={{ description: "...", links: [...] }}
* >
* <Hero ... />
* <section>...</section>
* </DocPage>
* ```
*/
import { ReactNode } from 'react';
import { Breadcrumb } from '@/components/docs/shared/Breadcrumb';
import { DocsTOC } from '@/components/docs/layout/DocsTOC';
import { SectionHeader, LinkCard, LinkCardGrid } from '@/components/docs/blocks';
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
/**
* Next step link card configuration
*/
export interface NextStepLink {
href: string;
title: string;
description: string;
accent: 'primary' | 'secondary' | 'success' | 'warning';
}
/**
* Table of Contents item
*/
export interface TocItem {
id: string;
text: string;
level: number;
}
/**
* Breadcrumb navigation item
*/
export interface BreadcrumbItem {
label: string;
href?: string;
}
/**
* DocPage component props
*/
export interface DocPageProps {
/** Breadcrumb navigation items (top of page) */
breadcrumbItems: BreadcrumbItem[];
/** Table of contents items (right sidebar) */
tocItems: TocItem[];
/** Next steps section configuration */
nextSteps: {
/** Optional description text before link cards */
description?: string;
/** Link cards to display (typically 2) */
links: NextStepLink[];
};
/** Page content (Hero, sections, etc.) */
children: ReactNode;
}
/**
* Documentation Page Layout Component
*
* Wraps documentation content with consistent structure including
* breadcrumbs, TOC sidebar, and next steps section.
*/
export const DocPage = ({
breadcrumbItems,
tocItems,
nextSteps,
children,
}: DocPageProps) => {
return (
<ThreeColumnLayout
center={
<article className="max-w-3xl mx-auto px-6 lg:px-12 py-12">
{/* Breadcrumb Navigation */}
<Breadcrumb items={breadcrumbItems} />
{/* Page Content (Hero + Sections) */}
{children}
{/* Next Steps Section */}
<section id="next-steps" className="mb-12">
<SectionHeader level={2} id="next-steps" className="mb-6">
Next Steps
</SectionHeader>
{nextSteps.description && (
<p className="text-gray-300 leading-relaxed mb-6">
{nextSteps.description}
</p>
)}
<LinkCardGrid columns={2}>
{nextSteps.links.map((link, index) => (
<LinkCard
key={index}
href={link.href}
title={link.title}
description={link.description}
accent={link.accent}
/>
))}
</LinkCardGrid>
</section>
</article>
}
right={
<div className="border-l border-white/10 bg-slate-950/30 backdrop-blur-sm sticky top-12 h-[calc(100vh-3rem)] overflow-y-auto">
<DocsTOC items={tocItems} />
</div>
}
/>
);
};

View File

@ -0,0 +1,64 @@
'use client';
/**
* Documentation Layout - Production Version
*
* Based on Variant A: Clean & Minimal with enhancements
* This is the production-ready version combining the best aspects
*
* Layout Structure:
* - Left Sidebar: Thin (256px) with minimal design
* - Content Area: Wide margins (max-w-3xl) for comfortable reading
* - Right TOC: Subtle and unobtrusive (224px)
*
* Responsive Behavior:
* - Mobile (<768px): Single column, sidebars become overlays
* - Tablet (768px-1024px): Content + TOC only
* - Desktop (>1024px): Full three-column layout
*
* Design Characteristics:
* - Generous whitespace
* - Subtle borders and shadows
* - Focus on content clarity
* - Minimal visual noise
* - Accessibility compliant (WCAG 2.1 AA)
*/
import { ReactNode } from 'react';
interface DocsLayoutProps {
sidebar: ReactNode;
children: ReactNode;
toc: ReactNode;
}
export const DocsLayout = ({ sidebar, children, toc }: DocsLayoutProps) => {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950">
{/* Animated gradient background (matching landing page) */}
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div className="absolute top-1/4 -left-1/4 w-96 h-96 bg-purple-600/10 rounded-full blur-3xl animate-pulse"></div>
<div className="absolute bottom-1/4 -right-1/4 w-96 h-96 bg-cyan-600/10 rounded-full blur-3xl animate-pulse delay-700"></div>
</div>
<div className="relative z-10 flex">
{/* Left Sidebar - Thin, minimal design */}
<aside className="hidden lg:block w-64 border-r border-white/10 bg-slate-950/50 backdrop-blur-sm sticky top-0 h-screen overflow-y-auto">
{sidebar}
</aside>
{/* Main Content Area - Wide margins for comfortable reading */}
<main className="flex-1 min-w-0">
<div className="max-w-3xl mx-auto px-6 lg:px-12 py-12">
{children}
</div>
</main>
{/* Right TOC - Subtle and unobtrusive */}
<aside className="hidden xl:block w-56 border-l border-white/10 bg-slate-950/30 backdrop-blur-sm sticky top-0 h-screen overflow-y-auto">
{toc}
</aside>
</div>
</div>
);
};

View File

@ -0,0 +1,181 @@
'use client';
/**
* Documentation Sidebar - Production Version
*
* Based on Variant A with FIXED active states
*
* Key Improvements:
* - Consistent active state styling for both parent and child items
* - Clean left border indicator (no background boxes)
* - Parent active: purple left border + white text
* - Child active: purple left border + white text (NO background)
*
* Features:
* - Thin sidebar with subtle hover states
* - Collapsible section groups
* - Minimal icons (chevron for expandable items)
* - Clean, uncluttered appearance
*
* Navigation Structure:
* - Getting Started
* - API Reference (collapsible)
* - Guides (collapsible)
* - Examples
*/
import { useState } from 'react';
interface NavItem {
label: string;
href: string;
icon?: string;
children?: NavItem[];
}
interface DocsSidebarProps {
currentPath: string;
}
const navigationItems: NavItem[] = [
{
label: 'Getting Started',
href: '/docs',
icon: '🚀',
},
{
label: 'API Reference',
href: '/docs/api',
icon: '📚',
children: [
{ label: 'Text to Image', href: '/docs/api/text-to-image' },
{ label: 'Upload', href: '/docs/api/upload' },
{ label: 'Images', href: '/docs/api/images' },
],
},
{
label: 'Guides',
href: '/docs/guides',
icon: '📖',
children: [
{ label: 'Authentication', href: '/docs/guides/authentication' },
{ label: 'Error Handling', href: '/docs/guides/error-handling' },
{ label: 'Rate Limits', href: '/docs/guides/rate-limits' },
],
},
{
label: 'Examples',
href: '/docs/examples',
icon: '💡',
},
];
export const DocsSidebar = ({ currentPath }: DocsSidebarProps) => {
const [expandedSections, setExpandedSections] = useState<string[]>(['API Reference', 'Guides']);
const toggleSection = (label: string) => {
setExpandedSections((prev) =>
prev.includes(label) ? prev.filter((item) => item !== label) : [...prev, label]
);
};
const isActive = (href: string) => currentPath === href;
const isExpanded = (label: string) => expandedSections.includes(label);
return (
<nav className="p-6" aria-label="Documentation navigation">
{/* Logo/Title */}
<div className="mb-8">
<h2 className="text-lg font-semibold text-white">Documentation</h2>
<p className="text-xs text-gray-500 mt-1">API Reference</p>
</div>
{/* Navigation Items */}
<ul className="space-y-1">
{navigationItems.map((item) => {
const hasChildren = item.children && item.children.length > 0;
const expanded = isExpanded(item.label);
const active = isActive(item.href);
return (
<li key={item.label}>
{/* Parent Item */}
<div className="relative">
{active && (
<div className="absolute left-0 top-0 bottom-0 w-0.5 bg-purple-500 rounded-r"></div>
)}
<a
href={item.href}
onClick={
hasChildren
? (e) => {
e.preventDefault();
toggleSection(item.label);
}
: undefined
}
className={`
flex items-center justify-between px-3 py-2 rounded-lg text-sm transition-colors
${active ? 'bg-purple-500/10 text-white font-medium' : 'text-gray-400 hover:text-white hover:bg-white/5'}
`}
>
<span className="flex items-center gap-2">
{item.icon && <span className="text-base">{item.icon}</span>}
<span>{item.label}</span>
</span>
{hasChildren && (
<svg
className={`w-4 h-4 transition-transform ${expanded ? 'rotate-90' : ''}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
)}
</a>
</div>
{/* Children Items - FIXED ACTIVE STATE */}
{hasChildren && expanded && (
<ul className="ml-6 mt-1 space-y-1 border-l border-slate-800">
{item.children!.map((child) => {
const childActive = isActive(child.href);
return (
<li key={child.label}>
<a
href={child.href}
className={`
block px-3 py-1.5 text-sm transition-colors ml-[-2px]
${
childActive
? 'text-white font-medium border-l-2 border-purple-500 bg-transparent pl-4'
: 'text-gray-500 hover:text-gray-300 border-l-2 border-transparent pl-4'
}
`}
>
{child.label}
</a>
</li>
);
})}
</ul>
)}
</li>
);
})}
</ul>
{/* Bottom Links */}
<div className="mt-12 pt-6 border-t border-slate-800">
<ul className="space-y-2 text-sm">
<li>
<a href="/" className="text-gray-500 hover:text-gray-300 transition-colors">
Back to home
</a>
</li>
</ul>
</div>
</nav>
);
};

View File

@ -0,0 +1,102 @@
'use client';
/**
* Table of Contents - Production Version
*
* Based on Variant A: Clean & Minimal (no changes needed)
*
* Features:
* - Small indicator dots for section levels
* - Smooth scroll to section
* - Active section highlighting (purple text)
* - Sticky positioning
* - Minimal visual weight
*
* Behavior:
* - Extracts H2 and H3 headings from content
* - Tracks scroll position to highlight active section
* - Click to smooth scroll to section
*/
import { useEffect, useState } from 'react';
interface TocItem {
id: string;
text: string;
level: number;
}
interface DocsTOCProps {
items: TocItem[];
}
export const DocsTOC = ({ items }: DocsTOCProps) => {
const [activeId, setActiveId] = useState<string>('');
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveId(entry.target.id);
}
});
},
{ rootMargin: '-20% 0px -35% 0px' }
);
items.forEach((item) => {
const element = document.getElementById(item.id);
if (element) observer.observe(element);
});
return () => observer.disconnect();
}, [items]);
const scrollToSection = (id: string) => {
const element = document.getElementById(id);
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
};
if (items.length === 0) {
return null;
}
return (
<nav className="p-6 sticky top-6" aria-label="Table of contents">
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-4">
On This Page
</h3>
<ul className="space-y-2.5 text-sm">
{items.map((item) => {
const isActive = activeId === item.id;
const isH3 = item.level === 3;
return (
<li key={item.id} className={isH3 ? 'ml-4' : ''}>
<button
onClick={() => scrollToSection(item.id)}
className={`
flex items-start gap-2 text-left w-full transition-colors group
${isActive ? 'text-purple-400' : 'text-gray-500 hover:text-gray-300'}
`}
>
{/* Indicator dot */}
<span
className={`
flex-shrink-0 w-1 h-1 rounded-full mt-2 transition-colors
${isActive ? 'bg-purple-400' : 'bg-gray-600 group-hover:bg-gray-400'}
`}
></span>
<span className="line-clamp-2">{item.text}</span>
</button>
</li>
);
})}
</ul>
</nav>
);
};

View File

@ -0,0 +1,73 @@
'use client';
/**
* Breadcrumb Navigation Component
*
* Displays hierarchical navigation path for documentation pages.
* Follows WCAG 2.1 AA accessibility guidelines with proper ARIA labels.
*
* Design:
* - Consistent with Banatie dark theme
* - Gray text with white hover for inactive items
* - White text for current page
* - Chevron separators between items
*/
interface BreadcrumbItem {
label: string;
href?: string;
}
interface BreadcrumbProps {
items: BreadcrumbItem[];
}
export const Breadcrumb = ({ items }: BreadcrumbProps) => {
return (
<nav aria-label="Breadcrumb" className="mb-6">
<ol className="flex items-center gap-2 text-sm">
{items.map((item, index) => {
const isLast = index === items.length - 1;
return (
<li key={index} className="flex items-center gap-2">
{item.href && !isLast ? (
<a
href={item.href}
className="text-gray-400 hover:text-white transition-colors"
aria-current={isLast ? 'page' : undefined}
>
{item.label}
</a>
) : (
<span
className={isLast ? 'text-white font-medium' : 'text-gray-400'}
aria-current={isLast ? 'page' : undefined}
>
{item.label}
</span>
)}
{!isLast && (
<svg
className="w-4 h-4 text-gray-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 5l7 7-7 7"
/>
</svg>
)}
</li>
);
})}
</ol>
</nav>
);
};

View File

@ -0,0 +1,69 @@
'use client';
/**
* CodeBlock Component
*
* Wrapper for displaying syntax-highlighted code blocks in documentation.
* Uses the existing Banatie design pattern from the landing page.
*
* Design Features:
* - MacOS-style window with traffic light dots
* - Dark slate background with backdrop blur
* - Copy button in top-right
* - Scrollable code area
* - Optional language label
*/
import { useState } from 'react';
interface CodeBlockProps {
code: string;
language?: string;
filename?: string;
showLineNumbers?: boolean;
}
export const CodeBlock = ({
code,
language = 'bash',
filename,
showLineNumbers = false,
}: CodeBlockProps) => {
const [copied, setCopied] = useState(false);
const handleCopy = () => {
navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="bg-slate-950/50 rounded-xl border border-slate-700 overflow-hidden my-4">
{/* Header with traffic lights and copy button */}
<div className="flex items-center justify-between bg-slate-900/50 px-4 py-2.5 border-b border-slate-700">
<div className="flex items-center gap-2">
<div className="flex gap-1.5">
<div className="w-3 h-3 rounded-full bg-red-500/50"></div>
<div className="w-3 h-3 rounded-full bg-yellow-500/50"></div>
<div className="w-3 h-3 rounded-full bg-green-500/50"></div>
</div>
{(filename || language) && (
<span className="text-sm text-gray-500 ml-2">{filename || language}</span>
)}
</div>
<button
onClick={handleCopy}
className="px-3 py-1 text-xs bg-purple-600/20 hover:bg-purple-600/30 text-purple-400 rounded transition-colors"
aria-label="Copy code"
>
{copied ? 'Copied!' : 'Copy'}
</button>
</div>
{/* Code content */}
<pre className="p-4 text-sm text-gray-300 overflow-x-auto">
<code>{code}</code>
</pre>
</div>
);
};

View File

@ -0,0 +1,173 @@
'use client';
/**
* Code Block Expanded Modal Component
*
* Full-screen code viewer for better readability
* Features:
* - Full-screen overlay with dark backdrop
* - Centered modal with max-width constraint
* - Language tabs for switching between languages
* - Close button (X) in top-right
* - Escape key support
* - Larger code text in expanded mode
* - Copy button with feedback
* - Language badge
*
* Usage:
* <CodeBlockExpanded
* isOpen={isOpen}
* onClose={() => setIsOpen(false)}
* codes={{ curl: '...', javascript: '...', python: '...', go: '...' }}
* initialLanguage="curl"
* filename="example.js"
* />
*/
import { useState, useEffect } from 'react';
type Language = 'curl' | 'javascript' | 'python' | 'go';
interface CodeBlockExpandedProps {
isOpen: boolean;
onClose: () => void;
codes: Record<Language, string>;
initialLanguage: Language;
filename?: string;
}
export const CodeBlockExpanded = ({
isOpen,
onClose,
codes,
initialLanguage,
filename,
}: CodeBlockExpandedProps) => {
const [currentLanguage, setCurrentLanguage] = useState<Language>(initialLanguage);
const [copied, setCopied] = useState(false);
// Update language when modal opens
useEffect(() => {
if (isOpen) {
setCurrentLanguage(initialLanguage);
}
}, [isOpen, initialLanguage]);
// Handle escape key
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleEscape);
// Prevent body scroll when modal is open
document.body.style.overflow = 'hidden';
}
return () => {
document.removeEventListener('keydown', handleEscape);
document.body.style.overflow = 'unset';
};
}, [isOpen, onClose]);
// Copy to clipboard
const copyCode = () => {
navigator.clipboard.writeText(codes[currentLanguage]);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
if (!isOpen) return null;
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm"
onClick={onClose}
role="dialog"
aria-modal="true"
aria-label="Expanded code view"
>
{/* Modal Content */}
<div
className="relative w-full max-w-7xl max-h-[90vh] overflow-hidden rounded-2xl bg-slate-950 border border-slate-700 shadow-2xl"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-slate-700 bg-slate-900/80">
{/* Left: Language Tabs */}
<div className="flex items-center gap-3">
<span className="text-sm font-medium text-gray-400 mr-2">Language:</span>
<div className="flex gap-2">
{(['curl', 'javascript', 'python', 'go'] as Language[]).map((lang) => (
<button
key={lang}
onClick={() => setCurrentLanguage(lang)}
className={`px-3 py-1.5 text-xs rounded-lg transition-colors ${
currentLanguage === lang
? 'bg-purple-500/20 text-purple-300 font-semibold'
: 'text-gray-400 hover:text-white hover:bg-slate-800'
}`}
>
{lang === 'javascript' ? 'JavaScript' : lang.charAt(0).toUpperCase() + lang.slice(1)}
</button>
))}
</div>
</div>
{/* Right: Actions */}
<div className="flex items-center gap-2">
{/* Copy Button */}
<button
onClick={copyCode}
className={`px-4 py-1.5 text-xs rounded-lg transition-colors ${
copied
? 'bg-green-500/20 text-green-400'
: 'bg-purple-600/20 hover:bg-purple-600/30 text-purple-400'
}`}
>
{copied ? '✓ Copied!' : 'Copy'}
</button>
{/* Close Button */}
<button
onClick={onClose}
className="p-2 text-gray-400 hover:text-white transition-colors rounded-lg hover:bg-slate-800"
aria-label="Close expanded view"
>
<svg
className="w-5 h-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
</div>
{/* Code Content - Larger text, better spacing */}
<div className="overflow-auto max-h-[calc(90vh-6rem)] p-8 bg-slate-950/50">
<pre className="text-base text-gray-300 leading-relaxed">
<code>{codes[currentLanguage]}</code>
</pre>
</div>
{/* Hint Text */}
<div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 px-3 py-1.5 bg-slate-900/90 border border-slate-700 rounded-full">
<p className="text-xs text-gray-400">
Press <kbd className="px-1.5 py-0.5 bg-slate-800 rounded text-purple-400">Esc</kbd> to close
</p>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,93 @@
'use client';
/**
* Enhanced Table Component
*
* Features:
* - Larger font: text-base for cells (more readable)
* - Better padding: py-4 px-6 (spacious)
* - Full-width with proper column distribution
* - Hover states for rows
* - Responsive: horizontal scroll on mobile
* - Optional sortable headers (UI only, no logic yet)
*
* Usage:
* <Table
* headers={['Name', 'Type', 'Required', 'Description']}
* rows={[
* ['prompt', 'string', 'Yes', 'The text prompt'],
* ['filename', 'string', 'No', 'Output filename'],
* ]}
* />
*/
import { ReactNode } from 'react';
interface TableProps {
headers: string[];
rows: (string | ReactNode)[][];
sortable?: boolean;
}
export const Table = ({ headers, rows, sortable = false }: TableProps) => {
return (
<div className="overflow-x-auto rounded-xl border border-slate-700 bg-slate-900/50 backdrop-blur-sm">
<table className="w-full">
{/* Header */}
<thead>
<tr className="border-b border-slate-700 bg-slate-800/50">
{headers.map((header, idx) => (
<th
key={idx}
className="py-4 px-6 text-left text-sm font-semibold text-white whitespace-nowrap"
>
<div className="flex items-center gap-2">
{header}
{sortable && (
<button
className="text-gray-500 hover:text-gray-300 transition-colors"
aria-label={`Sort by ${header}`}
>
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"
/>
</svg>
</button>
)}
</div>
</th>
))}
</tr>
</thead>
{/* Body */}
<tbody>
{rows.map((row, rowIdx) => (
<tr
key={rowIdx}
className="border-b border-slate-800 last:border-b-0 hover:bg-slate-800/30 transition-colors"
>
{row.map((cell, cellIdx) => (
<td
key={cellIdx}
className="py-4 px-6 text-base text-gray-300 align-top"
>
{cell}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
};

View File

@ -0,0 +1,90 @@
'use client';
/**
* TipBox Component - Dual Style System
*
* Provides two distinct styles for callouts/notes:
*
* 1. Compact Style (Variant A inspired):
* - Small emoji icon on left
* - Compact padding (p-4)
* - Smaller text (text-sm)
* - Subtle background with thin border
* - Best for minor notes and tips
*
* 2. Prominent Style (Adapted from Variant C):
* - NO icon
* - Larger padding (p-6)
* - Larger text (text-base)
* - Gradient border with soft glow
* - More visual weight
* - Best for important warnings and security notices
*
* Usage:
* <TipBox variant="compact" type="info">
* This is a compact tip
* </TipBox>
*
* <TipBox variant="prominent" type="warning">
* This is an important security warning
* </TipBox>
*/
import { ReactNode } from 'react';
type TipType = 'info' | 'warning' | 'success';
type TipVariant = 'compact' | 'prominent';
interface TipBoxProps {
children: ReactNode;
variant?: TipVariant;
type?: TipType;
}
const icons: Record<TipType, string> = {
info: '💡',
warning: '⚠️',
success: '✓',
};
const compactStyles: Record<TipType, string> = {
info: 'bg-purple-500/10 border-purple-500/20 text-purple-300',
warning: 'bg-amber-500/10 border-amber-500/20 text-amber-300',
success: 'bg-green-500/10 border-green-500/20 text-green-300',
};
const prominentStyles: Record<TipType, string> = {
info: 'bg-gradient-to-br from-purple-500/5 via-cyan-500/5 to-purple-500/5 border-purple-500/30 text-gray-300 shadow-lg shadow-purple-500/10',
warning: 'bg-gradient-to-br from-amber-500/5 via-orange-500/5 to-amber-500/5 border-amber-500/30 text-gray-300 shadow-lg shadow-amber-500/10',
success: 'bg-gradient-to-br from-green-500/5 via-emerald-500/5 to-green-500/5 border-green-500/30 text-gray-300 shadow-lg shadow-green-500/10',
};
export const TipBox = ({ children, variant = 'compact', type = 'info' }: TipBoxProps) => {
const isCompact = variant === 'compact';
const icon = icons[type];
const styleClass = isCompact ? compactStyles[type] : prominentStyles[type];
if (isCompact) {
return (
<div
className={`flex gap-3 p-4 rounded-xl border ${styleClass}`}
role="note"
aria-label={`${type} note`}
>
<span className="text-lg flex-shrink-0">{icon}</span>
<div className="text-sm leading-relaxed">{children}</div>
</div>
);
}
// Prominent style
return (
<div
className={`p-6 rounded-2xl border ${styleClass}`}
role="alert"
aria-label={`${type} alert`}
>
<div className="text-base leading-relaxed">{children}</div>
</div>
);
};

View File

@ -0,0 +1,119 @@
'use client';
/**
* Three Column Layout - Core Wireframe Component
*
* Provides a consistent three-column layout structure used across the application.
* This is the single source of truth for column widths, breakpoints, and responsive behavior.
*
* Column Structure:
* - Left: w-64 (256px), hidden until lg breakpoint
* - Center: flex-1 (flexible, takes remaining space)
* - Right: w-56 (224px), hidden until xl breakpoint
*
* Usage Examples:
*
* 1. SubsectionNav (3 columns):
* <ThreeColumnLayout
* left={<Logo />}
* center={<NavItems />}
* right={<UserMenu />}
* />
*
* 2. Docs Layout (2 columns - left + center):
* <ThreeColumnLayout
* left={<DocsSidebar />}
* center={<DocPage />}
* />
*
* 3. Doc Page (2 columns - center + right):
* <ThreeColumnLayout
* center={<Article />}
* right={<DocsTOC />}
* />
*
* Design Principles:
* - Responsive breakpoints match Tailwind defaults (lg: 1024px, xl: 1280px)
* - Column widths ensure visual alignment across all usage contexts
* - Flexible center column adapts to available space
* - Optional columns enable 1, 2, or 3 column layouts
*/
import { ReactNode } from 'react';
/**
* Props for ThreeColumnLayout component
*/
export interface ThreeColumnLayoutProps {
/** Left column content (w-64, hidden until lg breakpoint) */
left?: ReactNode;
/** Center column content (flex-1, always visible) */
center: ReactNode;
/** Right column content (w-56, hidden until xl breakpoint) */
right?: ReactNode;
/** Additional classes for left column */
leftClassName?: string;
/** Additional classes for center column */
centerClassName?: string;
/** Additional classes for right column */
rightClassName?: string;
/** Additional classes for container */
containerClassName?: string;
/** Breakpoint for showing left column (default: 'lg') */
leftBreakpoint?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
/** Breakpoint for showing right column (default: 'xl') */
rightBreakpoint?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
}
/**
* Three Column Layout Component
*
* Core wireframe for consistent three-column layouts across the application.
* Handles responsive behavior and provides customization via className props.
*/
export const ThreeColumnLayout = ({
left,
center,
right,
leftClassName = '',
centerClassName = '',
rightClassName = '',
containerClassName = '',
leftBreakpoint = 'lg',
rightBreakpoint = 'xl',
}: ThreeColumnLayoutProps) => {
// Generate responsive visibility classes
const leftHidden = `hidden ${leftBreakpoint}:block`;
const rightHidden = `hidden ${rightBreakpoint}:block`;
return (
<div className={`flex ${containerClassName}`}>
{/* Left Column - w-64 (256px) */}
{left && (
<div className={`${leftHidden} w-64 shrink-0 ${leftClassName}`}>
{left}
</div>
)}
{/* Center Column - flex-1 (flexible) */}
<div className={`flex-1 min-w-0 ${centerClassName}`}>
{center}
</div>
{/* Right Column - w-56 (224px) */}
{right && (
<div className={`${rightHidden} w-56 shrink-0 ${rightClassName}`}>
{right}
</div>
)}
</div>
);
};

View File

@ -0,0 +1,419 @@
'use client';
/**
* Subsection Navigation Component
*
* Reusable navigation bar for documentation and other subsections
* Features:
* - Dark nav bar with decorative wave line
* - Active state indicator (purple color)
* - Optional API Key input on the right
* - Responsive (hamburger menu on mobile)
* - Three-column layout matching docs structure
* - Customizable left/right slots
*
* Uses ThreeColumnLayout for consistent alignment with docs pages.
* Columns align with docs layout at all screen widths.
*
* Usage:
* <SubsectionNav
* items={[...]}
* currentPath="/docs/final"
* showApiKeyInput={true}
* leftSlot={<Logo />}
* rightSlot={<UserMenu />}
* />
*/
import { useState, useEffect, useRef, ReactNode } from 'react';
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
interface NavItem {
label: string;
href: string;
}
interface SubsectionNavProps {
items: NavItem[];
currentPath: string;
ctaText?: string;
ctaHref?: string;
onCtaClick?: () => void;
showApiKeyInput?: boolean;
/** Optional content for left column (w-64, hidden until lg) */
leftSlot?: ReactNode;
/** Optional content for right column (w-56, hidden until xl). If not provided and showApiKeyInput is true, API key input is used. */
rightSlot?: ReactNode;
}
const API_KEY_STORAGE = 'banatie_docs_api_key';
export const SubsectionNav = ({
items,
currentPath,
showApiKeyInput = false,
leftSlot,
rightSlot,
}: SubsectionNavProps) => {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [apiKey, setApiKey] = useState('');
const [isApiKeyExpanded, setIsApiKeyExpanded] = useState(false);
const [keyVisible, setKeyVisible] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const isActive = (href: string) => currentPath.startsWith(href);
// Load API key from localStorage
useEffect(() => {
if (showApiKeyInput) {
const stored = localStorage.getItem(API_KEY_STORAGE);
if (stored) setApiKey(stored);
}
}, [showApiKeyInput]);
// Handle API key changes
const handleApiKeyChange = (value: string) => {
setApiKey(value);
if (value) {
localStorage.setItem(API_KEY_STORAGE, value);
} else {
localStorage.removeItem(API_KEY_STORAGE);
}
// Dispatch event to notify widgets
window.dispatchEvent(new CustomEvent('apiKeyChanged', { detail: value }));
};
// Handle clear API key
const handleClear = () => {
setApiKey('');
localStorage.removeItem(API_KEY_STORAGE);
window.dispatchEvent(new CustomEvent('apiKeyChanged', { detail: '' }));
};
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsApiKeyExpanded(false);
}
};
if (isApiKeyExpanded) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isApiKeyExpanded]);
// Listen for custom event to expand API key from widgets
useEffect(() => {
const handleExpandApiKey = () => {
setIsApiKeyExpanded(true);
// Scroll to top to show nav
window.scrollTo({ top: 0, behavior: 'smooth' });
};
window.addEventListener('expandApiKeyInput', handleExpandApiKey);
return () => {
window.removeEventListener('expandApiKeyInput', handleExpandApiKey);
};
}, []);
// Desktop API Key Input Component
const apiKeyComponent = showApiKeyInput && (
<div className="hidden md:block relative" ref={dropdownRef}>
<button
onClick={() => setIsApiKeyExpanded(!isApiKeyExpanded)}
className="flex items-center gap-2 px-3 py-1.5 bg-slate-800/50 hover:bg-slate-800 border border-purple-500/30 hover:border-purple-500/50 rounded-lg transition-all"
aria-label="Toggle API key input"
>
<div
className={`w-2 h-2 rounded-full transition-colors ${
apiKey ? 'bg-green-400' : 'bg-amber-400'
}`}
></div>
<span className="text-xs text-gray-300 font-medium">
🔑 {apiKey ? 'API Key Set' : 'API Key'}
</span>
<svg
className={`w-3 h-3 text-gray-400 transition-transform ${
isApiKeyExpanded ? 'rotate-180' : ''
}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
{/* Dropdown Card */}
{isApiKeyExpanded && (
<div className="absolute top-full right-0 mt-2 w-80 p-4 bg-slate-900/95 backdrop-blur-sm border border-purple-500/30 rounded-xl shadow-2xl z-50 animate-fade-in">
<div className="flex items-start justify-between mb-3">
<div>
<h3 className="text-sm font-semibold text-white mb-1">API Key</h3>
<p className="text-xs text-gray-400">
Enter once, use across all examples
</p>
</div>
<button
onClick={() => setIsApiKeyExpanded(false)}
className="w-8 h-8 rounded-lg bg-slate-800 hover:bg-slate-700 text-gray-400 hover:text-white flex items-center justify-center transition-colors"
aria-label="Close API key input"
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<div className="mb-3">
<label className="text-xs text-gray-500 mb-1 block">Your API Key</label>
<div className="flex gap-2 mb-2">
<input
type={keyVisible ? 'text' : 'password'}
value={apiKey}
onChange={(e) => handleApiKeyChange(e.target.value)}
placeholder="Enter your API key"
className="flex-1 px-3 py-2 bg-slate-800 border border-slate-700 focus:border-purple-500 focus:ring-1 focus:ring-purple-500 rounded-lg text-sm text-gray-300 font-mono placeholder-gray-500 focus:outline-none transition-colors"
/>
<button
onClick={() => setKeyVisible(!keyVisible)}
className="px-3 py-2 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-lg text-gray-400 hover:text-white transition-colors"
aria-label={keyVisible ? 'Hide API key' : 'Show API key'}
>
{keyVisible ? (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
/>
</svg>
) : (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
/>
</svg>
)}
</button>
</div>
{apiKey && (
<button
onClick={handleClear}
className="w-full px-3 py-2 bg-red-900/20 hover:bg-red-900/30 border border-red-700/50 hover:border-red-600 rounded-lg text-red-400 text-xs font-medium transition-colors"
>
Clear API Key
</button>
)}
</div>
<div className="text-xs text-gray-500">
<p>Stored locally in your browser</p>
</div>
</div>
)}
</div>
);
return (
<nav className="sticky top-0 z-40 bg-slate-950/80 backdrop-blur-sm border-b border-white/10">
{/* Three-Column Layout */}
<ThreeColumnLayout
left={leftSlot}
center={
<div className="max-w-7xl mx-auto px-6">
<div className="flex items-center justify-between h-12">
{/* Desktop Navigation Items */}
<div className="hidden md:flex items-center gap-8">
{items.map((item) => {
const active = isActive(item.href);
return (
<a
key={item.href}
href={item.href}
className={`
py-3 text-sm font-medium transition-colors
${active ? 'text-purple-400' : 'text-gray-400 hover:text-white'}
`}
>
{item.label}
</a>
);
})}
</div>
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center ml-auto">
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="p-2 text-gray-400 hover:text-white transition-colors"
aria-label="Toggle menu"
>
<svg
className="w-6 h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
{mobileMenuOpen ? (
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
) : (
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
)}
</svg>
</button>
</div>
</div>
</div>
}
right={rightSlot || apiKeyComponent}
/>
{/* Decorative Wave Line */}
<div className="absolute bottom-0 left-0 right-0 h-px overflow-hidden">
<svg
className="absolute inset-0 w-full h-full"
viewBox="0 0 1200 2"
preserveAspectRatio="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient id="wave-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="rgb(147, 51, 234)" stopOpacity="0.3" />
<stop offset="50%" stopColor="rgb(8, 145, 178)" stopOpacity="0.5" />
<stop offset="100%" stopColor="rgb(147, 51, 234)" stopOpacity="0.3" />
</linearGradient>
</defs>
<path
d="M0,1 Q300,0 600,1 T1200,1"
stroke="url(#wave-gradient)"
strokeWidth="1"
fill="none"
className="animate-pulse"
/>
</svg>
</div>
{/* Mobile Menu Overlay */}
{mobileMenuOpen && (
<div className="md:hidden border-t border-white/10 bg-slate-900/95 backdrop-blur-sm">
<div className="px-6 py-4 space-y-2">
{items.map((item) => {
const active = isActive(item.href);
return (
<a
key={item.href}
href={item.href}
onClick={() => setMobileMenuOpen(false)}
className={`
block px-4 py-3 rounded-lg text-sm font-medium transition-colors
${active ? 'bg-purple-500/10 text-purple-400' : 'text-gray-400 hover:text-white hover:bg-white/5'}
`}
>
{item.label}
</a>
);
})}
{/* API Key Input in Mobile Menu */}
{showApiKeyInput && (
<div className="pt-4 mt-4 border-t border-white/10">
<h4 className="text-xs font-semibold text-white mb-2">API Key</h4>
<p className="text-xs text-gray-400 mb-3">
Enter once, use across all examples
</p>
<div className="flex gap-2 mb-2">
<input
type={keyVisible ? 'text' : 'password'}
value={apiKey}
onChange={(e) => handleApiKeyChange(e.target.value)}
placeholder="Enter your API key"
className="flex-1 px-3 py-2 bg-slate-800 border border-slate-700 focus:border-purple-500 focus:ring-1 focus:ring-purple-500 rounded-lg text-sm text-gray-300 font-mono placeholder-gray-500 focus:outline-none transition-colors"
/>
<button
onClick={() => setKeyVisible(!keyVisible)}
className="px-3 py-2 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-lg text-gray-400 hover:text-white transition-colors"
aria-label={keyVisible ? 'Hide API key' : 'Show API key'}
>
{keyVisible ? (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
/>
</svg>
) : (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
/>
</svg>
)}
</button>
</div>
{apiKey && (
<button
onClick={handleClear}
className="w-full px-3 py-2 bg-red-900/20 hover:bg-red-900/30 border border-red-700/50 hover:border-red-600 rounded-lg text-red-400 text-xs font-medium transition-colors"
>
Clear API Key
</button>
)}
<p className="text-xs text-gray-500 mt-2">Stored locally in your browser</p>
</div>
)}
</div>
</div>
)}
</nav>
);
};