Track B — Auth & User System (complete): - User registration with bcrypt + Turnstile verification - JWT access/refresh token flow with httpOnly cookie rotation - Redis refresh token blocklist for logout - User profile + settings update endpoints (username, email) - API key generation with bcrypt hashing (ff_key_ prefix) - BYOK key management with AES-256-GCM encryption at rest - Free tier rate limiting (5 shaders/month) - Tier-gated endpoints (Pro/Studio for BYOK, API keys, bounty posting) Track C — Shader Submission & Renderer (complete): - GLSL validator: entry point check, banned extensions, infinite loop detection, brace balancing, loop bound warnings, code length limits - Puppeteer/headless Chromium renderer with Shadertoy-compatible uniform injection (iTime, iResolution, iMouse), WebGL2 with SwiftShader fallback - Shader compilation error detection via page title signaling - Thumbnail capture at t=1s, preview frame at t=duration - Renderer client service for API→renderer HTTP communication - Shader submission pipeline: validate GLSL → create record → enqueue render job - Desire fulfillment linking on shader submit - Re-validation and re-render on shader code update - Fork endpoint copies code, tags, metadata, enqueues new render Track D — Frontend Shell (complete): - React 18 + Vite + TypeScript + Tailwind CSS + TanStack Query + Zustand - Dark theme with custom fracta color palette and surface tones - Responsive layout with sticky navbar, gradient branding - Auth: Login + Register pages with JWT token management - API client with automatic 401 refresh interceptor - ShaderCanvas: Full WebGL2 renderer component with Shadertoy uniforms, mouse tracking, ResizeObserver, debounced recompilation, error callbacks - GLSL Editor: Split pane (code textarea + live preview), 400ms debounced preview, metadata panel (description, tags, type), GLSL validation errors, shader publish flow, fork-from-existing support - Feed: Infinite scroll with IntersectionObserver sentinel, dwell time tracking, skeleton loading states, empty state with CTA - Explore: Search + tag filter + sort tabs (trending/new/top), grid layout - ShaderDetail: Full-screen preview, vote controls, view source toggle, fork button - Bounties: Desire queue list sorted by heat score, status badges, tip display - BountyDetail: Single desire view with style hints, fulfill CTA - Profile: User header with avatar initial, shader grid - Settings: Account info, API key management (create/revoke/copy), subscription tiers - Generate: AI generation UI stub with prompt input, style controls, example prompts 76 files, ~5,700 lines of application code.
106 lines
3.7 KiB
TypeScript
106 lines
3.7 KiB
TypeScript
/**
|
|
* AI Generation page — prompt-to-shader interface.
|
|
* Stub for M5 — shows UI with "coming soon" state.
|
|
*/
|
|
|
|
import { useState } from 'react';
|
|
import { useAuthStore } from '@/stores/auth';
|
|
import { Link } from 'react-router-dom';
|
|
|
|
export default function Generate() {
|
|
const { isAuthenticated, user } = useAuthStore();
|
|
const [prompt, setPrompt] = useState('');
|
|
|
|
return (
|
|
<div className="max-w-3xl mx-auto px-4 py-6">
|
|
<div className="text-center mb-8">
|
|
<h1 className="text-2xl font-bold">AI Shader Generator</h1>
|
|
<p className="text-gray-400 mt-2">
|
|
Describe what you want to see and let AI write the shader for you.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="card p-6">
|
|
{/* Prompt input */}
|
|
<div className="mb-6">
|
|
<label className="block text-sm text-gray-400 mb-2">What do you want to see?</label>
|
|
<textarea
|
|
value={prompt}
|
|
onChange={(e) => setPrompt(e.target.value)}
|
|
className="input min-h-[100px] resize-y font-normal"
|
|
placeholder="A flowing aurora borealis with deep purples and greens, slowly morphing..."
|
|
/>
|
|
</div>
|
|
|
|
{/* Style controls */}
|
|
<div className="grid grid-cols-3 gap-4 mb-6">
|
|
<div>
|
|
<label className="block text-xs text-gray-500 mb-1">Chaos Level</label>
|
|
<input type="range" min="0" max="100" defaultValue="50"
|
|
className="w-full accent-fracta-500" />
|
|
</div>
|
|
<div>
|
|
<label className="block text-xs text-gray-500 mb-1">Color Temperature</label>
|
|
<select className="input text-sm">
|
|
<option>Warm</option>
|
|
<option>Cool</option>
|
|
<option>Neutral</option>
|
|
<option>Monochrome</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="block text-xs text-gray-500 mb-1">Motion Type</label>
|
|
<select className="input text-sm">
|
|
<option>Fluid</option>
|
|
<option>Geometric</option>
|
|
<option>Pulsing</option>
|
|
<option>Static</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Generate button / status */}
|
|
<div className="text-center">
|
|
<button
|
|
disabled
|
|
className="btn-primary opacity-60 cursor-not-allowed px-8 py-3 text-lg"
|
|
>
|
|
✨ Generate Shader
|
|
</button>
|
|
<p className="text-sm text-gray-500 mt-3">
|
|
AI generation is coming in M5. For now, use the{' '}
|
|
<Link to="/editor" className="text-fracta-400 hover:text-fracta-300">editor</Link>{' '}
|
|
to write shaders manually.
|
|
</p>
|
|
|
|
{isAuthenticated() && user && (
|
|
<p className="text-xs text-gray-600 mt-2">
|
|
Credits remaining: {user.ai_credits_remaining}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Teaser examples */}
|
|
<div className="mt-8">
|
|
<h2 className="text-sm font-medium text-gray-400 mb-3">Example prompts (coming soon)</h2>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
{[
|
|
"Ragdoll physics but dark and slow",
|
|
"Underwater caustics with bioluminescent particles",
|
|
"Infinite fractal zoom through a crystal cathedral",
|
|
"VHS glitch art with neon pink scanlines",
|
|
].map((example) => (
|
|
<button
|
|
key={example}
|
|
onClick={() => setPrompt(example)}
|
|
className="text-left p-3 bg-surface-2 hover:bg-surface-3 rounded-lg text-sm text-gray-400 transition-colors"
|
|
>
|
|
"{example}"
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|