feat: final variant ready

This commit is contained in:
Oleg Proskurin 2025-10-14 00:54:34 +07:00
parent da47634805
commit 1d1a88d073
12 changed files with 2122 additions and 0 deletions

View File

@ -0,0 +1,255 @@
'use client';
/**
* API Reference: Text to Image - Final Variant
*
* Features:
* - SubsectionNav at top
* - InteractiveAPIWidgetFinal with expand + success styling
* - Enhanced tables for parameters and error codes
* - Compact TipBox for parameter descriptions
* - Simplified Next Steps
*/
import { DocsLayoutFinal } from '@/components/docs/final/DocsLayoutFinal';
import { DocsSidebarFinal } from '@/components/docs/final/DocsSidebarFinal';
import { DocsTOCFinal } from '@/components/docs/final/DocsTOCFinal';
import { SubsectionNav } from '@/components/shared/SubsectionNav';
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { InteractiveAPIWidgetFinal } from '@/components/docs/final/InteractiveAPIWidgetFinal';
import { Breadcrumb } from '@/components/docs/shared/Breadcrumb';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
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 navItems = [
{ label: 'Documentation', href: '/docs/final' },
{ label: 'Demo', href: '/demo' },
{ label: 'Examples', href: '/docs/final/examples' },
];
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 (
<>
{/* Subsection Navigation */}
<SubsectionNav
items={navItems}
currentPath="/docs/final/api/text-to-image"
ctaText="Join Beta"
ctaHref="/signup"
/>
<DocsLayoutFinal
sidebar={<DocsSidebarFinal currentPath="/docs/final/api/text-to-image" />}
toc={<DocsTOCFinal items={tocItems} />}
>
<Breadcrumb
items={[
{ label: 'Documentation', href: '/docs' },
{ label: 'API Reference', href: '/docs/final/api' },
{ label: 'Text to Image' },
]}
/>
{/* Hero Section */}
<div className="mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-white mb-4">Text to Image</h1>
<p className="text-xl text-gray-400 leading-relaxed">
Generate high-quality images from text prompts using AI-powered models.
</p>
</div>
{/* Overview */}
<section id="overview" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-4">Overview</h2>
<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 <code className="px-1.5 py-0.5 bg-slate-800 rounded text-purple-400">autoEnhance</code>
to let AI improve your prompts for better image quality.
</TipBox>
</section>
{/* Endpoint */}
<section id="endpoint" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-4">Endpoint</h2>
<div className="p-4 bg-slate-900/50 border border-slate-700 rounded-xl">
<div className="flex items-center gap-3 mb-2">
<span className="px-3 py-1 bg-green-600/20 text-green-400 text-xs font-bold rounded">
POST
</span>
<code className="text-sm text-white">/api/text-to-image</code>
</div>
<p className="text-xs text-gray-400">
Base URL: <code className="text-purple-400">https://api.banatie.com</code>
</p>
</div>
</section>
{/* Parameters */}
<section id="parameters" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-6">Parameters</h2>
<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) => [
<code key="name" className="text-purple-400">{param.name}</code>,
<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, <code className="px-1.5 py-0.5 bg-slate-800 rounded text-purple-400">filename</code> is
auto-generated, <code className="px-1.5 py-0.5 bg-slate-800 rounded text-purple-400">aspectRatio</code> defaults
to "1:1", and <code className="px-1.5 py-0.5 bg-slate-800 rounded text-purple-400">autoEnhance</code> is false.
</TipBox>
</div>
</section>
{/* Response */}
<section id="response" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-4">Response</h2>
<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">
<h2 className="text-3xl font-bold text-white mb-6">Error Codes</h2>
<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={[
[
<code key="code" className="text-red-400">400</code>,
'Bad Request',
'Missing or invalid parameters in the request body',
],
[
<code key="code" className="text-red-400">401</code>,
'Unauthorized',
'Missing or invalid API key in X-API-Key header',
],
[
<code key="code" className="text-red-400">429</code>,
'Rate Limit',
'Too many requests. Check rate limit headers for retry timing',
],
[
<code key="code" className="text-red-400">500</code>,
'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">
<h2 className="text-3xl font-bold text-white mb-4">Try It Live</h2>
<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>
<InteractiveAPIWidgetFinal
endpoint="/api/text-to-image"
method="POST"
description="Generate an image from a text prompt"
parameters={parameters}
/>
</section>
{/* Next Steps */}
<section id="next-steps" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-6">Next Steps</h2>
<div className="grid md:grid-cols-2 gap-4">
<a
href="/docs/final/api/upload"
className="p-5 bg-gradient-to-br from-purple-500/10 to-transparent border border-slate-700/50 rounded-xl hover:border-purple-500/40 transition-colors group"
>
<h3 className="text-lg font-semibold text-white mb-2 group-hover:text-purple-400 transition-colors">
Upload API
</h3>
<p className="text-sm text-gray-400">
Learn how to upload reference images for image-to-image generation.
</p>
</a>
<a
href="/docs/final/guides/error-handling"
className="p-5 bg-gradient-to-br from-cyan-500/10 to-transparent border border-slate-700/50 rounded-xl hover:border-cyan-500/40 transition-colors group"
>
<h3 className="text-lg font-semibold text-white mb-2 group-hover:text-cyan-400 transition-colors">
Error Handling
</h3>
<p className="text-sm text-gray-400">
Best practices for handling errors and retries in production.
</p>
</a>
</div>
</section>
</DocsLayoutFinal>
</>
);
}

View File

@ -0,0 +1,336 @@
'use client';
/**
* Authentication Guide - Final Variant
*
* Features:
* - SubsectionNav at top
* - Prominent TipBox for security warnings
* - Compact TipBox for general tips
* - Enhanced tables for rate limits and key types
* - Simplified Next Steps
*/
import { DocsLayoutFinal } from '@/components/docs/final/DocsLayoutFinal';
import { DocsSidebarFinal } from '@/components/docs/final/DocsSidebarFinal';
import { DocsTOCFinal } from '@/components/docs/final/DocsTOCFinal';
import { SubsectionNav } from '@/components/shared/SubsectionNav';
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { Breadcrumb } from '@/components/docs/shared/Breadcrumb';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
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 },
];
const navItems = [
{ label: 'Documentation', href: '/docs/final' },
{ label: 'Demo', href: '/demo' },
{ label: 'Examples', href: '/docs/final/examples' },
];
export default function AuthenticationGuidePage() {
return (
<>
{/* Subsection Navigation */}
<SubsectionNav
items={navItems}
currentPath="/docs/final/guides/authentication"
ctaText="Join Beta"
ctaHref="/signup"
/>
<DocsLayoutFinal
sidebar={<DocsSidebarFinal currentPath="/docs/final/guides/authentication" />}
toc={<DocsTOCFinal items={tocItems} />}
>
<Breadcrumb
items={[
{ label: 'Documentation', href: '/docs' },
{ label: 'Guides', href: '/docs/final/guides' },
{ label: 'Authentication' },
]}
/>
{/* Hero Section */}
<div className="mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-white mb-4">Authentication</h1>
<p className="text-xl text-gray-400 leading-relaxed">
Learn how to authenticate with the Banatie API using API keys, manage rate limits, and
implement security best practices.
</p>
</div>
{/* Overview */}
<section id="overview" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-4">Overview</h2>
<p className="text-gray-300 leading-relaxed mb-6">
Banatie uses API keys to authenticate requests. All API endpoints require authentication
via the <code className="px-1.5 py-0.5 bg-slate-800 rounded text-purple-400">X-API-Key</code> 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/final" 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">
<h2 className="text-3xl font-bold text-white mb-6">API Keys</h2>
<div id="key-types" className="mb-8">
<h3 className="text-xl font-semibold text-white mb-4">Key Types</h3>
<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={[
[
<code key="type" className="text-purple-400">Master Key</code>,
'Full admin access, can create/revoke keys',
<span key="exp" className="text-green-400">Never expires</span>,
'Server-side admin operations, key management',
],
[
<code key="type" className="text-cyan-400">Project Key</code>,
'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">
<h3 className="text-xl font-semibold text-white mb-4">Creating Keys</h3>
<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">
<h2 className="text-3xl font-bold text-white mb-4">Using API Keys</h2>
<p className="text-gray-300 leading-relaxed mb-4">
Include your API key in the <code className="px-1.5 py-0.5 bg-slate-800 rounded text-purple-400">X-API-Key</code> 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:{' '}
<code className="px-1.5 py-0.5 bg-slate-800 rounded text-purple-400">BANATIE_API_KEY</code>
</TipBox>
</div>
</section>
{/* Rate Limits */}
<section id="rate-limits" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-6">Rate Limits</h2>
<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={[
[
<code key="type" className="text-purple-400">Master Key</code>,
<span key="limit" className="text-green-400">Unlimited</span>,
'N/A',
'N/A',
],
[
<code key="type" className="text-cyan-400">Project Key (Free)</code>,
<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>,
],
[
<code key="type" className="text-cyan-400">Project Key (Pro)</code>,
<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 <code className="px-1.5 py-0.5 bg-slate-800 rounded text-red-400">429 Too Many Requests</code> 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">
<h2 className="text-3xl font-bold text-white mb-6">Security Best Practices</h2>
<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>
{/* Next Steps */}
<section id="next-steps" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-6">Next Steps</h2>
<div className="grid md:grid-cols-2 gap-4">
<a
href="/docs/final/api/text-to-image"
className="p-5 bg-gradient-to-br from-purple-500/10 to-transparent border border-slate-700/50 rounded-xl hover:border-purple-500/40 transition-colors group"
>
<h3 className="text-lg font-semibold text-white mb-2 group-hover:text-purple-400 transition-colors">
Start Generating Images
</h3>
<p className="text-sm text-gray-400">
Explore the Text to Image API and start building your integration.
</p>
</a>
<a
href="/docs/final/guides/error-handling"
className="p-5 bg-gradient-to-br from-cyan-500/10 to-transparent border border-slate-700/50 rounded-xl hover:border-cyan-500/40 transition-colors group"
>
<h3 className="text-lg font-semibold text-white mb-2 group-hover:text-cyan-400 transition-colors">
Error Handling Guide
</h3>
<p className="text-sm text-gray-400">
Learn how to handle authentication errors and implement retry logic.
</p>
</a>
</div>
</section>
</DocsLayoutFinal>
</>
);
}

View File

@ -0,0 +1,225 @@
'use client';
/**
* Getting Started Page - Final Variant
*
* Production-ready documentation landing page
* Features:
* - SubsectionNav at top
* - Both TipBox styles (compact + prominent)
* - Enhanced tables
* - Simplified Next Steps (2 cards only)
* - Clean, accessible design
*/
import { DocsLayoutFinal } from '@/components/docs/final/DocsLayoutFinal';
import { DocsSidebarFinal } from '@/components/docs/final/DocsSidebarFinal';
import { DocsTOCFinal } from '@/components/docs/final/DocsTOCFinal';
import { SubsectionNav } from '@/components/shared/SubsectionNav';
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { Breadcrumb } from '@/components/docs/shared/Breadcrumb';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
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 },
];
const navItems = [
{ label: 'Documentation', href: '/docs/final' },
{ label: 'Demo', href: '/demo' },
{ label: 'Examples', href: '/docs/final/examples' },
];
export default function GettingStartedPageFinal() {
return (
<>
{/* Subsection Navigation */}
<SubsectionNav
items={navItems}
currentPath="/docs/final"
ctaText="Join Beta"
ctaHref="/signup"
/>
<DocsLayoutFinal
sidebar={<DocsSidebarFinal currentPath="/docs/final" />}
toc={<DocsTOCFinal items={tocItems} />}
>
<Breadcrumb items={[{ label: 'Documentation', href: '/docs' }, { label: 'Getting Started' }]} />
{/* Hero Section */}
<div className="mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-white mb-4">Getting Started</h1>
<p className="text-xl text-gray-400 leading-relaxed">
Welcome to the Banatie API documentation. Learn how to integrate AI-powered image
generation into your applications in minutes.
</p>
</div>
{/* Introduction */}
<section id="introduction" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-4">Introduction</h2>
<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/final/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">
<h2 className="text-3xl font-bold text-white mb-6">Quick Start</h2>
<div id="installation" className="mb-8">
<h3 className="text-xl font-semibold text-white mb-3">Installation</h3>
<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">
<h3 className="text-xl font-semibold text-white mb-3">Authentication</h3>
<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">
<h2 className="text-3xl font-bold text-white mb-4">Your First Request</h2>
<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>
<div className="relative p-4 bg-green-500/5 border border-green-500/30 rounded-xl shadow-lg shadow-green-500/10">
<div className="absolute top-3 right-3">
<span className="px-2 py-1 text-xs bg-green-500/20 text-green-400 rounded-full">
200 Success
</span>
</div>
<pre className="text-xs text-gray-300 overflow-x-auto mt-6">
<code>{`{
"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..."
}
}
}`}</code>
</pre>
</div>
</div>
</section>
{/* Next Steps - Simplified to 2 Cards */}
<section id="next-steps" className="mb-12">
<h2 className="text-3xl font-bold text-white mb-6">Next Steps</h2>
<p className="text-gray-300 leading-relaxed mb-6">
Now that you have generated your first image, explore these resources to build more advanced integrations:
</p>
<div className="grid md:grid-cols-2 gap-4">
<a
href="/docs/final/api/text-to-image"
className="p-5 bg-gradient-to-br from-purple-500/10 to-transparent border border-slate-700/50 rounded-xl hover:border-purple-500/40 transition-colors group"
>
<h3 className="text-lg font-semibold text-white mb-2 group-hover:text-purple-400 transition-colors">
API Reference
</h3>
<p className="text-sm text-gray-400">
Explore all available endpoints, parameters, and response formats.
</p>
</a>
<a
href="/docs/final/guides/authentication"
className="p-5 bg-gradient-to-br from-cyan-500/10 to-transparent border border-slate-700/50 rounded-xl hover:border-cyan-500/40 transition-colors group"
>
<h3 className="text-lg font-semibold text-white mb-2 group-hover:text-cyan-400 transition-colors">
Authentication Guide
</h3>
<p className="text-sm text-gray-400">
Learn about API keys, rate limits, and security best practices.
</p>
</a>
</div>
</section>
</DocsLayoutFinal>
</>
);
}

View File

@ -53,6 +53,113 @@ export default function DocsIndexPage() {
</p>
</div>
{/* Final Variant - Recommended */}
<div className="mb-12">
<div className="text-center mb-6">
<span className="inline-block px-4 py-1.5 rounded-full bg-gradient-to-r from-purple-500 to-cyan-500 text-white text-sm font-semibold mb-2">
RECOMMENDED
</span>
<h2 className="text-2xl font-bold text-white">Production-Ready Design</h2>
</div>
<a
href="/docs/final"
className="group block p-10 bg-gradient-to-br from-purple-500/20 via-cyan-500/20 to-purple-500/20 border-4 border-purple-500/50 rounded-3xl hover:border-purple-500/80 transition-all duration-300 shadow-2xl hover:shadow-purple-500/40 hover:scale-[1.02]"
>
<div className="flex flex-col md:flex-row items-center gap-8">
<div className="flex-shrink-0 text-6xl"></div>
<div className="flex-1 text-center md:text-left">
<h3 className="text-3xl font-bold text-white mb-3 group-hover:text-purple-300 transition-colors">
Final Variant
</h3>
<h4 className="text-xl font-semibold text-purple-300 mb-4">
Best of All Worlds - Clean, Enhanced & Production-Ready
</h4>
<p className="text-base text-gray-300 mb-6 leading-relaxed max-w-3xl">
The perfect combination: Variant A's clean aesthetic enhanced with expandable code blocks, success/error response styling, clickable image URLs, two tip box styles, enhanced tables, and simplified navigation. This is the variant we recommend for production use.
</p>
<div className="grid md:grid-cols-3 gap-4 mb-6">
<div className="flex items-center gap-2 text-sm text-gray-300">
<svg className="w-5 h-5 text-purple-400" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
<span>Clean Variant A base</span>
</div>
<div className="flex items-center gap-2 text-sm text-gray-300">
<svg className="w-5 h-5 text-cyan-400" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
<span>Expandable code blocks</span>
</div>
<div className="flex items-center gap-2 text-sm text-gray-300">
<svg className="w-5 h-5 text-green-400" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
<span>Success/error styling</span>
</div>
<div className="flex items-center gap-2 text-sm text-gray-300">
<svg className="w-5 h-5 text-purple-400" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
<span>Two tip box styles</span>
</div>
<div className="flex items-center gap-2 text-sm text-gray-300">
<svg className="w-5 h-5 text-cyan-400" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
<span>Enhanced tables</span>
</div>
<div className="flex items-center gap-2 text-sm text-gray-300">
<svg className="w-5 h-5 text-green-400" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
<span>Reusable navigation</span>
</div>
</div>
<div className="flex items-center justify-center md:justify-start gap-3 text-purple-300 text-lg font-bold group-hover:gap-4 transition-all">
<span>Explore Final Variant</span>
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
</div>
</a>
</div>
{/* Other Variants for Reference */}
<div className="text-center mb-8">
<h3 className="text-xl font-semibold text-gray-400 mb-2">Other Variants (Reference)</h3>
<p className="text-sm text-gray-500">
Explore the original variants that inspired the final design
</p>
</div>
{/* Variant Cards */}
<div className="grid md:grid-cols-3 gap-8 mb-16">
{/* Variant A: Clean & Minimal */}

View File

@ -0,0 +1,64 @@
'use client';
/**
* Documentation Layout - Final Variant
*
* 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 DocsLayoutFinalProps {
sidebar: ReactNode;
children: ReactNode;
toc: ReactNode;
}
export const DocsLayoutFinal = ({ sidebar, children, toc }: DocsLayoutFinalProps) => {
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 - Final Variant
*
* 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 DocsSidebarFinalProps {
currentPath: string;
}
const navigationItems: NavItem[] = [
{
label: 'Getting Started',
href: '/docs/final',
icon: '🚀',
},
{
label: 'API Reference',
href: '/docs/final/api',
icon: '📚',
children: [
{ label: 'Text to Image', href: '/docs/final/api/text-to-image' },
{ label: 'Upload', href: '/docs/final/api/upload' },
{ label: 'Images', href: '/docs/final/api/images' },
],
},
{
label: 'Guides',
href: '/docs/final/guides',
icon: '📖',
children: [
{ label: 'Authentication', href: '/docs/final/guides/authentication' },
{ label: 'Error Handling', href: '/docs/final/guides/error-handling' },
{ label: 'Rate Limits', href: '/docs/final/guides/rate-limits' },
],
},
{
label: 'Examples',
href: '/docs/final/examples',
icon: '💡',
},
];
export const DocsSidebarFinal = ({ currentPath }: DocsSidebarFinalProps) => {
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">Final: Production</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 rounded-lg 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="/docs" className="text-gray-500 hover:text-gray-300 transition-colors">
Back to variants
</a>
</li>
</ul>
</div>
</nav>
);
};

View File

@ -0,0 +1,102 @@
'use client';
/**
* Table of Contents - Final Variant
*
* 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 DocsTOCFinalProps {
items: TocItem[];
}
export const DocsTOCFinal = ({ items }: DocsTOCFinalProps) => {
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,348 @@
'use client';
/**
* Interactive API Widget - Final Variant
*
* Enhanced version of Variant A with:
* 1. Expand button for full-screen code view
* 2. Success response styling (green accent)
* 3. Error response styling (red accent)
* 4. Clickable image URLs in response
* 5. Status badges (200 Success, Error)
*
* Features:
* - Multi-language code tabs (curl, JavaScript, Python, Go)
* - API key input field (persists via localStorage)
* - "Try It" button to execute live requests
* - Response viewer with enhanced styling
* - Clean, focused design
*/
import { useState, useEffect } from 'react';
import { CodeBlockExpanded } from '@/components/docs/shared/CodeBlockExpanded';
interface InteractiveAPIWidgetFinalProps {
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';
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
const API_KEY_STORAGE = 'banatie_docs_api_key';
export const InteractiveAPIWidgetFinal = ({
endpoint,
method,
description,
parameters = [],
}: InteractiveAPIWidgetFinalProps) => {
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);
// Parameter values
const [paramValues, setParamValues] = useState<Record<string, string>>({});
// Load API key from localStorage
useEffect(() => {
const stored = localStorage.getItem(API_KEY_STORAGE);
if (stored) setApiKey(stored);
// Initialize parameter default values
const defaults: Record<string, string> = {};
parameters.forEach((param) => {
if (param.defaultValue) {
defaults[param.name] = param.defaultValue;
}
});
setParamValues(defaults);
}, [parameters]);
// Save API key to localStorage
const handleApiKeyChange = (value: string) => {
setApiKey(value);
if (value) {
localStorage.setItem(API_KEY_STORAGE, value);
} else {
localStorage.removeItem(API_KEY_STORAGE);
}
};
// Generate code examples
const generateCode = (): string => {
const url = `${API_BASE_URL}${endpoint}`;
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"
"fmt"
"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');
return;
}
setIsExecuting(true);
setError(null);
try {
const body: Record<string, any> = {};
parameters.forEach((param) => {
if (paramValues[param.name]) {
body[param.name] = paramValues[param.name];
}
});
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,
});
const data = await res.json();
setResponse(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to execute request');
} finally {
setIsExecuting(false);
}
};
// Copy code to clipboard
const copyCode = () => {
navigator.clipboard.writeText(generateCode());
};
// Render response with clickable URLs
const renderResponse = (data: any): string => {
return JSON.stringify(data, null, 2);
};
// Check if response is success
const isSuccess = response && response.success === true;
return (
<>
<div className="my-8 bg-slate-900/50 backdrop-blur-sm border border-slate-700 rounded-2xl overflow-hidden">
{/* Header with API Key Input */}
<div className="p-4 border-b border-slate-700 bg-slate-900/80">
<div className="flex flex-col md:flex-row gap-3 items-start md:items-center justify-between">
<div>
<h3 className="text-sm font-semibold text-white mb-1">Try it out</h3>
<p className="text-xs text-gray-500">{description}</p>
</div>
<div className="flex-shrink-0 w-full md:w-64">
<input
type="password"
value={apiKey}
onChange={(e) => handleApiKeyChange(e.target.value)}
placeholder="Enter your API key"
className="w-full px-3 py-2 text-xs bg-slate-800 border border-slate-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
</div>
</div>
{/* Language Tabs with Expand Button */}
<div className="flex items-center justify-between bg-slate-950/50 px-4 py-2 border-b border-slate-700">
<div className="flex gap-2">
{(['curl', 'javascript', 'python', 'go'] as Language[]).map((lang) => (
<button
key={lang}
onClick={() => setLanguage(lang)}
className={`px-3 py-1 text-xs rounded transition-colors ${
language === lang
? 'bg-slate-700 text-white'
: 'text-gray-400 hover:text-white'
}`}
>
{lang === 'javascript' ? 'JavaScript' : lang.charAt(0).toUpperCase() + lang.slice(1)}
</button>
))}
</div>
<div className="flex gap-2">
<button
onClick={() => setIsExpanded(true)}
className="px-3 py-1 text-xs text-gray-400 hover:text-white transition-colors"
aria-label="Expand code view"
>
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"
/>
</svg>
</button>
<button
onClick={copyCode}
className="px-3 py-1 text-xs bg-purple-600/20 hover:bg-purple-600/30 text-purple-400 rounded transition-colors"
>
Copy
</button>
</div>
</div>
{/* Code Display */}
<div className="p-4 bg-slate-950/50">
<pre className="text-xs text-gray-300 overflow-x-auto">
<code>{generateCode()}</code>
</pre>
</div>
{/* Try It Button */}
<div className="p-4 border-t border-slate-700 bg-slate-900/50">
<button
onClick={executeRequest}
disabled={!apiKey || isExecuting}
className="w-full px-4 py-2.5 rounded-lg bg-gradient-to-r from-purple-600 to-cyan-600 text-white text-sm font-semibold hover:from-purple-500 hover:to-cyan-500 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
{isExecuting ? 'Executing...' : 'Try It'}
</button>
</div>
{/* Response Section - Enhanced with success/error styling */}
{(response || error) && (
<div className="border-t border-slate-700">
<div className="p-4 bg-slate-900/80">
<h4 className="text-sm font-semibold text-white mb-3">Response</h4>
{error ? (
// Error Response
<div className="relative p-4 bg-red-500/5 border border-red-500/30 rounded-xl">
<div className="absolute top-3 right-3">
<span className="px-2 py-1 text-xs bg-red-500/20 text-red-400 rounded-full">
Error
</span>
</div>
<p className="text-sm text-red-400 mt-6">{error}</p>
</div>
) : isSuccess ? (
// Success Response
<div className="relative p-4 bg-green-500/5 border border-green-500/30 rounded-xl shadow-lg shadow-green-500/10">
<div className="absolute top-3 right-3">
<span className="px-2 py-1 text-xs bg-green-500/20 text-green-400 rounded-full">
200 Success
</span>
</div>
<pre className="text-xs text-gray-300 overflow-x-auto mt-6">
<code>{renderResponse(response)}</code>
</pre>
</div>
) : (
// Normal Response
<pre className="p-3 bg-slate-950/50 rounded-lg text-xs text-gray-300 overflow-x-auto">
<code>{renderResponse(response)}</code>
</pre>
)}
</div>
</div>
)}
</div>
{/* Expanded Code Modal */}
<CodeBlockExpanded
isOpen={isExpanded}
onClose={() => setIsExpanded(false)}
code={generateCode()}
language={language}
filename={`example.${language === 'javascript' ? 'js' : language === 'python' ? 'py' : language}`}
/>
</>
);
};

View File

@ -0,0 +1,142 @@
'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
* - Close button (X) in top-right
* - Escape key support
* - Larger code text in expanded mode
* - Copy button still visible
* - Language badge
*
* Usage:
* <CodeBlockExpanded
* isOpen={isOpen}
* onClose={() => setIsOpen(false)}
* code={codeString}
* language="javascript"
* filename="example.js"
* />
*/
import { useEffect } from 'react';
interface CodeBlockExpandedProps {
isOpen: boolean;
onClose: () => void;
code: string;
language: string;
filename?: string;
}
export const CodeBlockExpanded = ({
isOpen,
onClose,
code,
language,
filename,
}: CodeBlockExpandedProps) => {
// 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(code);
};
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-6xl 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">
<div className="flex items-center gap-3">
{filename && (
<span className="text-sm font-medium text-white">{filename}</span>
)}
<span className="px-2 py-1 text-xs bg-purple-500/20 text-purple-400 rounded">
{language}
</span>
</div>
<div className="flex items-center gap-2">
{/* Copy Button */}
<button
onClick={copyCode}
className="px-3 py-1.5 text-xs bg-purple-600/20 hover:bg-purple-600/30 text-purple-400 rounded-lg transition-colors"
>
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 */}
<div className="overflow-auto max-h-[calc(90vh-5rem)] p-6 bg-slate-950/50">
<pre className="text-sm text-gray-300 leading-relaxed">
<code>{code}</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,179 @@
'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 underline/highlight)
* - "Join Beta" CTA button on the right
* - Responsive (hamburger menu on mobile)
* - Can be reused across landing app sections
*
* Usage:
* <SubsectionNav
* items={[...]}
* currentPath="/docs/final"
* ctaText="Join Beta"
* ctaHref="/signup"
* />
*/
import { useState } from 'react';
interface NavItem {
label: string;
href: string;
}
interface SubsectionNavProps {
items: NavItem[];
currentPath: string;
ctaText?: string;
ctaHref?: string;
onCtaClick?: () => void;
}
export const SubsectionNav = ({
items,
currentPath,
ctaText = 'Join Beta',
ctaHref = '/signup',
onCtaClick,
}: SubsectionNavProps) => {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const isActive = (href: string) => currentPath.startsWith(href);
return (
<nav className="sticky top-0 z-40 bg-slate-950/80 backdrop-blur-sm border-b border-white/10">
{/* Main Nav Container */}
<div className="max-w-7xl mx-auto px-6">
<div className="flex items-center justify-between h-16">
{/* Desktop Navigation */}
<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={`
relative py-5 text-sm font-medium transition-colors
${active ? 'text-purple-400' : 'text-gray-400 hover:text-white'}
`}
>
{item.label}
{/* Active Indicator */}
{active && (
<div className="absolute bottom-0 left-0 right-0 h-0.5 bg-purple-500 rounded-t-full"></div>
)}
</a>
);
})}
</div>
{/* CTA Button - Desktop */}
<div className="hidden md:block">
<a
href={ctaHref}
onClick={onCtaClick}
className="px-6 py-2.5 rounded-lg bg-gradient-to-r from-amber-600 to-orange-600 text-white text-sm font-semibold hover:from-amber-500 hover:to-orange-500 transition-all shadow-lg"
>
{ctaText}
</a>
</div>
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center gap-3">
<a
href={ctaHref}
onClick={onCtaClick}
className="px-4 py-2 rounded-lg bg-gradient-to-r from-amber-600 to-orange-600 text-white text-xs font-semibold"
>
{ctaText}
</a>
<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>
{/* 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>
);
})}
</div>
</div>
)}
</nav>
);
};