Compare commits
No commits in common. "658f1420db40d1b4e4449cd0e2d56f4e7dea90b2" and "da476348059f9259d7746ed79726732130ba749b" have entirely different histories.
658f1420db
...
da47634805
|
|
@ -1,255 +0,0 @@
|
||||||
'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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,336 +0,0 @@
|
||||||
'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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,225 +0,0 @@
|
||||||
'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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -53,113 +53,6 @@ export default function DocsIndexPage() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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 */}
|
{/* Variant Cards */}
|
||||||
<div className="grid md:grid-cols-3 gap-8 mb-16">
|
<div className="grid md:grid-cols-3 gap-8 mb-16">
|
||||||
{/* Variant A: Clean & Minimal */}
|
{/* Variant A: Clean & Minimal */}
|
||||||
|
|
|
||||||
|
|
@ -62,40 +62,3 @@ 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 */
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
'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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,181 +0,0 @@
|
||||||
'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 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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
'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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,453 +0,0 @@
|
||||||
'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 with proper defaults
|
|
||||||
const defaults: Record<string, string> = {
|
|
||||||
prompt: 'a futuristic city at sunset',
|
|
||||||
filename: 'demo',
|
|
||||||
aspectRatio: '16:9',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Override with parameter defaults if provided
|
|
||||||
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 '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate all language codes at once
|
|
||||||
const generateAllCodes = (): Record<Language, string> => {
|
|
||||||
const url = `${API_BASE_URL}${endpoint}`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
curl: `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"
|
|
||||||
}'`,
|
|
||||||
|
|
||||||
javascript: `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);`,
|
|
||||||
|
|
||||||
python: `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())`,
|
|
||||||
|
|
||||||
go: `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()
|
|
||||||
}`,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Execute API request
|
|
||||||
const executeRequest = async () => {
|
|
||||||
if (!apiKey) {
|
|
||||||
setError('Please enter your API key');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsExecuting(true);
|
|
||||||
setError(null);
|
|
||||||
setResponse(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Build proper request body
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Handle non-OK responses
|
|
||||||
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());
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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)}
|
|
||||||
codes={generateAllCodes()}
|
|
||||||
initialLanguage={language}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,173 +0,0 @@
|
||||||
'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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
'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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
'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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
'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 h-12">
|
|
||||||
{/* 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={`
|
|
||||||
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>
|
|
||||||
|
|
||||||
{/* 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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Loading…
Reference in New Issue