Initial DeepSite commit
Browse files- README.md +9 -6
- api-keys.html +189 -0
- dashboard.html +263 -0
- generator.html +193 -0
- index.html +826 -19
- marketplace.html +169 -0
- models.html +183 -0
- playground.html +191 -0
- signin.html +180 -0
- workflows.html +171 -0
README.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
colorTo: green
|
| 6 |
sdk: static
|
| 7 |
-
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: DeepSite Project
|
| 3 |
+
colorFrom: green
|
| 4 |
+
colorTo: pink
|
|
|
|
| 5 |
sdk: static
|
| 6 |
+
emoji: 🖥️
|
| 7 |
+
tags:
|
| 8 |
+
- deepsite-v4
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# DeepSite Project
|
| 12 |
+
|
| 13 |
+
This project has been created with [DeepSite](https://deepsite.hf.co) AI Vibe Coding.
|
api-keys.html
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>API Keys — NSFWStudio</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
| 9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| 10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 15 |
+
<script>
|
| 16 |
+
tailwind.config = { theme: { extend: { fontFamily: { sans: ['Inter','system-ui','sans-serif'], mono: ['JetBrains Mono','monospace'] }, colors: { bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)', fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)', secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)', destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)' } } } }
|
| 17 |
+
</script>
|
| 18 |
+
<style>
|
| 19 |
+
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
| 20 |
+
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
|
| 21 |
+
::selection { background: oklch(0.70 0.28 350 / 0.3); }
|
| 22 |
+
body::after { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat:repeat; background-size:256px 256px; }
|
| 23 |
+
.no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
| 24 |
+
.tabular-nums { font-variant-numeric: tabular-nums; }
|
| 25 |
+
</style>
|
| 26 |
+
</head>
|
| 27 |
+
<body class="antialiased">
|
| 28 |
+
<div id="root"></div>
|
| 29 |
+
<script type="text/babel">
|
| 30 |
+
const { useState } = React;
|
| 31 |
+
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
|
| 32 |
+
const ref = React.useRef(null);
|
| 33 |
+
React.useEffect(() => { if (ref.current) { const existing = ref.current.querySelector('svg'); if (existing) existing.remove(); const svg = lucide.createElement(lucide.icons[name]); if (svg) { svg.setAttribute('width',size); svg.setAttribute('height',size); svg.setAttribute('stroke-width',strokeWidth); ref.current.appendChild(svg); } } }, [name, size, className, strokeWidth]);
|
| 34 |
+
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
const navItems = [
|
| 38 |
+
{ name: 'Dashboard', icon: 'layout-dashboard', href: 'dashboard.html' },
|
| 39 |
+
{ name: 'Generator', icon: 'sparkles', href: 'generator.html' },
|
| 40 |
+
{ name: 'Playground', icon: 'code', href: 'playground.html' },
|
| 41 |
+
{ name: 'Models', icon: 'box', href: 'models.html' },
|
| 42 |
+
{ name: 'Workflows', icon: 'workflow', href: 'workflows.html' },
|
| 43 |
+
{ name: 'Marketplace', icon: 'shopping-bag', href: 'marketplace.html' },
|
| 44 |
+
{ name: 'API Keys', icon: 'key', href: 'api-keys.html', active: true },
|
| 45 |
+
{ divider: true },
|
| 46 |
+
{ name: 'Profile', icon: 'user', href: 'profile.html' },
|
| 47 |
+
{ name: 'Settings', icon: 'settings', href: 'settings.html' },
|
| 48 |
+
];
|
| 49 |
+
|
| 50 |
+
const Sidebar = ({ isOpen, setIsOpen }) => (
|
| 51 |
+
<>
|
| 52 |
+
<div className={`fixed inset-0 z-40 lg:hidden transition-all ${isOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
|
| 53 |
+
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setIsOpen(false)} />
|
| 54 |
+
</div>
|
| 55 |
+
<aside className={`fixed top-0 left-0 bottom-0 z-50 w-64 bg-surface border-r border-border flex flex-col transition-transform duration-300 lg:translate-x-0 ${isOpen ? 'translate-x-0' : '-translate-x-full'}`}>
|
| 56 |
+
<div className="p-4 flex items-center gap-2.5 border-b border-border"><div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div><span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span></div>
|
| 57 |
+
<nav className="flex-1 p-3 space-y-1 overflow-y-auto no-scrollbar">{navItems.map((item, i) => item.divider ? <div key={i} className="my-3 border-t border-border" /> : <a key={i} href={item.href} className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors ${item.active ? 'bg-primary/10 text-primary border border-primary/20' : 'text-muted hover:text-fg hover:bg-surface-2 border border-transparent'}`}><Icon name={item.icon} size={18} />{item.name}</a>)}</nav>
|
| 58 |
+
<div className="p-4 border-t border-border"><a href="documentation.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="book-open" size={18} />Documentation</a><a href="api-reference.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="file-code" size={18} />API Reference</a></div>
|
| 59 |
+
</aside>
|
| 60 |
+
</>
|
| 61 |
+
);
|
| 62 |
+
|
| 63 |
+
const App = () => {
|
| 64 |
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
| 65 |
+
const [keys, setKeys] = useState([
|
| 66 |
+
{ id: 1, name: 'Production Key', key: 'nfw_prod_sk_7f3a9b2c4d5e6f1a8b9c0d1e', created: 'Jan 15, 2025', lastUsed: '2 min ago', status: 'active' },
|
| 67 |
+
{ id: 2, name: 'Development Key', key: 'nfw_dev_sk_2e4b6c8d0f1a3b5c7d9e1f2a', created: 'Feb 3, 2025', lastUsed: '3 days ago', status: 'active' },
|
| 68 |
+
{ id: 3, name: 'Staging Environment', key: 'nfw_stg_sk_9a1b2c3d4e5f6a7b8c9d0e1f', created: 'Mar 22, 2025', lastUsed: 'Never', status: 'inactive' },
|
| 69 |
+
]);
|
| 70 |
+
const [showKey, setShowKey] = useState({});
|
| 71 |
+
const [showCreate, setShowCreate] = useState(false);
|
| 72 |
+
const [newKeyName, setNewKeyName] = useState('');
|
| 73 |
+
|
| 74 |
+
const toggleShowKey = (id) => setShowKey(prev => ({ ...prev, [id]: !prev[id] }));
|
| 75 |
+
|
| 76 |
+
const maskKey = (key) => key.substring(0, 12) + '••••••••••••••••••••';
|
| 77 |
+
|
| 78 |
+
return (
|
| 79 |
+
<div className="min-h-screen bg-bg">
|
| 80 |
+
<Sidebar isOpen={sidebarOpen} setIsOpen={setSidebarOpen} />
|
| 81 |
+
<div className="lg:ml-64">
|
| 82 |
+
<header className="sticky top-0 z-30 glass border-b border-border">
|
| 83 |
+
<div className="flex items-center justify-between px-4 sm:px-6 h-16">
|
| 84 |
+
<div className="flex items-center gap-4">
|
| 85 |
+
<button onClick={() => setSidebarOpen(true)} className="lg:hidden p-2 rounded-lg hover:bg-surface-2 text-muted"><Icon name="menu" size={20} /></button>
|
| 86 |
+
<h1 className="text-xl font-bold">API Keys</h1>
|
| 87 |
+
</div>
|
| 88 |
+
<div className="flex items-center gap-3">
|
| 89 |
+
<a href="profile.html" className="w-9 h-9 rounded-lg overflow-hidden border border-border"><img src="http://static.photos/people/100x100/10" alt="" className="w-full h-full object-cover" /></a>
|
| 90 |
+
</div>
|
| 91 |
+
</div>
|
| 92 |
+
</header>
|
| 93 |
+
|
| 94 |
+
<main className="p-4 sm:p-6 lg:p-8 max-w-5xl">
|
| 95 |
+
{/* Danger Zone Notice */}
|
| 96 |
+
<div className="rounded-xl p-4 bg-destructive/10 border border-destructive/20 mb-6 flex items-start gap-3">
|
| 97 |
+
<Icon name="alert-triangle" size={20} className="text-destructive shrink-0 mt-0.5" />
|
| 98 |
+
<div>
|
| 99 |
+
<h3 className="font-semibold text-fg text-sm">Keep your API keys secure</h3>
|
| 100 |
+
<p className="text-xs text-muted mt-0.5">Never share your secret keys in public repositories, client-side code, or public forums. If a key is compromised, revoke it immediately.</p>
|
| 101 |
+
</div>
|
| 102 |
+
</div>
|
| 103 |
+
|
| 104 |
+
<div className="flex items-center justify-between mb-6">
|
| 105 |
+
<div>
|
| 106 |
+
<p className="text-sm text-muted">Manage your API keys for programmatic access.</p>
|
| 107 |
+
</div>
|
| 108 |
+
<button onClick={() => setShowCreate(true)} className="px-4 py-2.5 rounded-xl bg-primary text-primary-foreground font-semibold text-sm hover:brightness-110 transition-all flex items-center gap-2">
|
| 109 |
+
<Icon name="plus" size={16} />Create Key
|
| 110 |
+
</button>
|
| 111 |
+
</div>
|
| 112 |
+
|
| 113 |
+
{/* Create dialog */}
|
| 114 |
+
{showCreate && (
|
| 115 |
+
<div className="rounded-2xl p-6 bg-surface border border-border mb-6">
|
| 116 |
+
<h3 className="font-semibold mb-4">Create New API Key</h3>
|
| 117 |
+
<div className="space-y-4">
|
| 118 |
+
<div>
|
| 119 |
+
<label className="block text-sm font-medium text-fg mb-2">Key Name</label>
|
| 120 |
+
<input type="text" value={newKeyName} onChange={e => setNewKeyName(e.target.value)} placeholder="e.g., Production Key" className="w-full max-w-md px-4 py-2.5 rounded-xl bg-surface-2 border border-border text-sm text-fg placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-primary/50" />
|
| 121 |
+
</div>
|
| 122 |
+
<div className="flex gap-3">
|
| 123 |
+
<button className="px-4 py-2.5 rounded-xl bg-primary text-primary-foreground font-semibold text-sm hover:brightness-110 transition-all" onClick={() => { if(newKeyName.trim()) { setKeys([...keys, { id: Date.now(), name: newKeyName, key: 'nfw_new_sk_' + Math.random().toString(36).substring(2, 18), created: 'Just now', lastUsed: 'Never', status: 'active' }]); setNewKeyName(''); setShowCreate(false); } }}>Create</button>
|
| 124 |
+
<button onClick={() => { setShowCreate(false); setNewKeyName(''); }} className="px-4 py-2.5 rounded-xl border border-border text-sm font-medium text-muted hover:text-fg hover:bg-surface-2 transition-colors">Cancel</button>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
</div>
|
| 128 |
+
)}
|
| 129 |
+
|
| 130 |
+
{/* Keys list */}
|
| 131 |
+
<div className="space-y-4">
|
| 132 |
+
{keys.map(k => (
|
| 133 |
+
<div key={k.id} className="rounded-2xl p-5 bg-surface border border-border hover:border-surface-3 transition-colors">
|
| 134 |
+
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4">
|
| 135 |
+
<div className="flex items-center gap-3">
|
| 136 |
+
<div className="w-10 h-10 rounded-xl bg-primary/10 flex items-center justify-center"><Icon name="key" size={18} className="text-primary" /></div>
|
| 137 |
+
<div>
|
| 138 |
+
<div className="font-semibold text-fg">{k.name}</div>
|
| 139 |
+
<div className="text-xs text-muted">Created {k.created}</div>
|
| 140 |
+
</div>
|
| 141 |
+
</div>
|
| 142 |
+
<span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold border ${k.status === 'active' ? 'bg-success/10 text-success border-success/20' : 'bg-muted/10 text-muted border-border'}`}>
|
| 143 |
+
<span className="w-1.5 h-1.5 rounded-full bg-current" />
|
| 144 |
+
{k.status === 'active' ? 'Active' : 'Inactive'}
|
| 145 |
+
</span>
|
| 146 |
+
</div>
|
| 147 |
+
<div className="flex items-center gap-2 p-3 rounded-xl bg-surface-2 border border-border mb-4">
|
| 148 |
+
<code className="flex-1 text-sm font-mono text-muted truncate">{showKey[k.id] ? k.key : maskKey(k.key)}</code>
|
| 149 |
+
<button onClick={() => toggleShowKey(k.id)} className="p-1.5 rounded-lg hover:bg-surface-3 text-muted hover:text-fg transition-colors shrink-0"><Icon name={showKey[k.id] ? 'eye-off' : 'eye'} size={16} /></button>
|
| 150 |
+
<button onClick={() => { navigator.clipboard?.writeText(k.key); }} className="p-1.5 rounded-lg hover:bg-surface-3 text-muted hover:text-fg transition-colors shrink-0"><Icon name="copy" size={16} /></button>
|
| 151 |
+
</div>
|
| 152 |
+
<div className="flex items-center justify-between text-xs text-muted">
|
| 153 |
+
<span>Last used: {k.lastUsed}</span>
|
| 154 |
+
<div className="flex items-center gap-2">
|
| 155 |
+
<button className="px-3 py-1.5 rounded-lg border border-border text-muted hover:text-fg hover:bg-surface-2 transition-colors">Regenerate</button>
|
| 156 |
+
<button className="px-3 py-1.5 rounded-lg border border-destructive/30 text-destructive hover:bg-destructive/10 transition-colors">Revoke</button>
|
| 157 |
+
</div>
|
| 158 |
+
</div>
|
| 159 |
+
</div>
|
| 160 |
+
))}
|
| 161 |
+
</div>
|
| 162 |
+
|
| 163 |
+
{/* Usage Stats */}
|
| 164 |
+
<div className="mt-8 rounded-2xl p-6 bg-surface border border-border">
|
| 165 |
+
<h2 className="font-semibold mb-4">Usage This Month</h2>
|
| 166 |
+
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
| 167 |
+
{[
|
| 168 |
+
{ label: 'Total Requests', value: '1.2M', change: '+23%' },
|
| 169 |
+
{ label: 'Tokens Used', value: '45.8M', change: '+18%' },
|
| 170 |
+
{ label: 'Avg Latency', value: '142ms', change: '-5%' },
|
| 171 |
+
{ label: 'Error Rate', value: '0.03%', change: '-12%' },
|
| 172 |
+
].map((s, i) => (
|
| 173 |
+
<div key={i} className="p-4 rounded-xl bg-surface-2 border border-border">
|
| 174 |
+
<div className="text-xs text-muted mb-1">{s.label}</div>
|
| 175 |
+
<div className="text-xl font-bold text-fg tabular-nums">{s.value}</div>
|
| 176 |
+
<div className="text-xs text-success mt-1">{s.change}</div>
|
| 177 |
+
</div>
|
| 178 |
+
))}
|
| 179 |
+
</div>
|
| 180 |
+
</div>
|
| 181 |
+
</main>
|
| 182 |
+
</div>
|
| 183 |
+
</div>
|
| 184 |
+
);
|
| 185 |
+
};
|
| 186 |
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
| 187 |
+
</script>
|
| 188 |
+
</body>
|
| 189 |
+
</html>
|
dashboard.html
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Dashboard — NSFWStudio</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
| 9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| 10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 15 |
+
<script>
|
| 16 |
+
tailwind.config = {
|
| 17 |
+
theme: {
|
| 18 |
+
extend: {
|
| 19 |
+
fontFamily: { sans: ['Inter','system-ui','sans-serif'], mono: ['JetBrains Mono','monospace'] },
|
| 20 |
+
colors: {
|
| 21 |
+
bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)',
|
| 22 |
+
fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)',
|
| 23 |
+
secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)',
|
| 24 |
+
destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)',
|
| 25 |
+
},
|
| 26 |
+
},
|
| 27 |
+
},
|
| 28 |
+
}
|
| 29 |
+
</script>
|
| 30 |
+
<style>
|
| 31 |
+
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
| 32 |
+
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
|
| 33 |
+
.gradient-text { background: linear-gradient(135deg, oklch(0.70 0.28 350), oklch(0.75 0.15 215)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
| 34 |
+
::selection { background: oklch(0.70 0.28 350 / 0.3); color: white; }
|
| 35 |
+
body::after { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat:repeat; background-size:256px 256px; }
|
| 36 |
+
.no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
| 37 |
+
.tabular-nums { font-variant-numeric: tabular-nums; }
|
| 38 |
+
</style>
|
| 39 |
+
</head>
|
| 40 |
+
<body class="antialiased">
|
| 41 |
+
<div id="root"></div>
|
| 42 |
+
<script type="text/babel">
|
| 43 |
+
const { useState } = React;
|
| 44 |
+
|
| 45 |
+
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
|
| 46 |
+
const ref = React.useRef(null);
|
| 47 |
+
React.useEffect(() => { if (ref.current) { const existing = ref.current.querySelector('svg'); if (existing) existing.remove(); const svg = lucide.createElement(lucide.icons[name]); if (svg) { svg.setAttribute('width',size); svg.setAttribute('height',size); svg.setAttribute('stroke-width',strokeWidth); ref.current.appendChild(svg); } } }, [name, size, className, strokeWidth]);
|
| 48 |
+
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
|
| 49 |
+
};
|
| 50 |
+
|
| 51 |
+
const navItems = [
|
| 52 |
+
{ name: 'Dashboard', icon: 'layout-dashboard', href: 'dashboard.html', active: true },
|
| 53 |
+
{ name: 'Generator', icon: 'sparkles', href: 'generator.html' },
|
| 54 |
+
{ name: 'Playground', icon: 'code', href: 'playground.html' },
|
| 55 |
+
{ name: 'Models', icon: 'box', href: 'models.html' },
|
| 56 |
+
{ name: 'Workflows', icon: 'workflow', href: 'workflows.html' },
|
| 57 |
+
{ name: 'Marketplace', icon: 'shopping-bag', href: 'marketplace.html' },
|
| 58 |
+
{ name: 'API Keys', icon: 'key', href: 'api-keys.html' },
|
| 59 |
+
{ divider: true },
|
| 60 |
+
{ name: 'Profile', icon: 'user', href: 'profile.html' },
|
| 61 |
+
{ name: 'Settings', icon: 'settings', href: 'settings.html' },
|
| 62 |
+
];
|
| 63 |
+
|
| 64 |
+
const Sidebar = ({ isOpen, setIsOpen }) => (
|
| 65 |
+
<>
|
| 66 |
+
{/* Mobile overlay */}
|
| 67 |
+
<div className={`fixed inset-0 z-40 lg:hidden transition-all ${isOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
|
| 68 |
+
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setIsOpen(false)} />
|
| 69 |
+
</div>
|
| 70 |
+
<aside className={`fixed top-0 left-0 bottom-0 z-50 w-64 bg-surface border-r border-border flex flex-col transition-transform duration-300 lg:translate-x-0 ${isOpen ? 'translate-x-0' : '-translate-x-full'}`}>
|
| 71 |
+
<div className="p-4 flex items-center gap-2.5 border-b border-border">
|
| 72 |
+
<div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div>
|
| 73 |
+
<span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span>
|
| 74 |
+
</div>
|
| 75 |
+
<nav className="flex-1 p-3 space-y-1 overflow-y-auto no-scrollbar">
|
| 76 |
+
{navItems.map((item, i) => item.divider ? <div key={i} className="my-3 border-t border-border" /> : (
|
| 77 |
+
<a key={i} href={item.href} className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors ${item.active ? 'bg-primary/10 text-primary border border-primary/20' : 'text-muted hover:text-fg hover:bg-surface-2 border border-transparent'}`}>
|
| 78 |
+
<Icon name={item.icon} size={18} />{item.name}
|
| 79 |
+
</a>
|
| 80 |
+
))}
|
| 81 |
+
</nav>
|
| 82 |
+
<div className="p-4 border-t border-border">
|
| 83 |
+
<a href="documentation.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors">
|
| 84 |
+
<Icon name="book-open" size={18} />Documentation
|
| 85 |
+
</a>
|
| 86 |
+
<a href="api-reference.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors">
|
| 87 |
+
<Icon name="file-code" size={18} />API Reference
|
| 88 |
+
</a>
|
| 89 |
+
</div>
|
| 90 |
+
</aside>
|
| 91 |
+
</>
|
| 92 |
+
);
|
| 93 |
+
|
| 94 |
+
const App = () => {
|
| 95 |
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
| 96 |
+
|
| 97 |
+
const stats = [
|
| 98 |
+
{ label: 'Generations', value: '12,847', change: '+14.2%', icon: 'image', color: 'text-primary', bg: 'bg-primary/10' },
|
| 99 |
+
{ label: 'Revenue', value: '$8,492', change: '+23.1%', icon: 'dollar-sign', color: 'text-success', bg: 'bg-success/10' },
|
| 100 |
+
{ label: 'Followers', value: '2,341', change: '+8.7%', icon: 'users', color: 'text-secondary', bg: 'bg-secondary/10' },
|
| 101 |
+
{ label: 'API Calls', value: '284K', change: '+31.5%', icon: 'activity', color: 'text-accent', bg: 'bg-accent/10' },
|
| 102 |
+
];
|
| 103 |
+
|
| 104 |
+
const recentGens = [
|
| 105 |
+
{ title: 'Neon Cityscape', model: 'DreamVision XL', time: '2m ago', status: 'completed', img: 'http://static.photos/cyberpunk/200x200/5' },
|
| 106 |
+
{ title: 'Cyberpunk Girl', model: 'SDXL 1.0', time: '5m ago', status: 'completed', img: 'http://static.photos/people/200x200/6' },
|
| 107 |
+
{ title: 'Dark Forest', model: 'MidJourney v6', time: '8m ago', status: 'completed', img: 'http://static.photos/nature/200x200/7' },
|
| 108 |
+
{ title: 'Sci-Fi Portrait', model: 'DreamVision XL', time: '12m ago', status: 'completed', img: 'http://static.photos/technology/200x200/8' },
|
| 109 |
+
{ title: 'Anime Warrior', model: 'Anime Diffusion', time: '15m ago', status: 'processing', img: 'http://static.photos/people/200x200/9' },
|
| 110 |
+
];
|
| 111 |
+
|
| 112 |
+
const activity = [
|
| 113 |
+
{ text: 'New follower: @dark_pixel', time: '1m ago', icon: 'user-plus', color: 'text-secondary' },
|
| 114 |
+
{ text: 'Your model earned $49.00', time: '3m ago', icon: 'dollar-sign', color: 'text-success' },
|
| 115 |
+
{ text: 'Workflow "Auto Tag" completed', time: '7m ago', icon: 'check-circle', color: 'text-primary' },
|
| 116 |
+
{ text: 'Comment on "Neon Cityscape"', time: '10m ago', icon: 'message-circle', color: 'text-accent' },
|
| 117 |
+
{ text: 'API key rotation reminder', time: '1h ago', icon: 'key', color: 'text-muted' },
|
| 118 |
+
];
|
| 119 |
+
|
| 120 |
+
const topModels = [
|
| 121 |
+
{ name: 'DreamVision XL', runs: '45.2K', revenue: '$2,340', trend: '+12%' },
|
| 122 |
+
{ name: 'CyberPunk v3', runs: '28.1K', revenue: '$1,890', trend: '+8%' },
|
| 123 |
+
{ name: 'Anime Diffusion', runs: '19.7K', revenue: '$1,230', trend: '+21%' },
|
| 124 |
+
];
|
| 125 |
+
|
| 126 |
+
return (
|
| 127 |
+
<div className="min-h-screen bg-bg">
|
| 128 |
+
<Sidebar isOpen={sidebarOpen} setIsOpen={setSidebarOpen} />
|
| 129 |
+
<div className="lg:ml-64">
|
| 130 |
+
{/* Top bar */}
|
| 131 |
+
<header className="sticky top-0 z-30 glass border-b border-border">
|
| 132 |
+
<div className="flex items-center justify-between px-4 sm:px-6 h-16">
|
| 133 |
+
<div className="flex items-center gap-4">
|
| 134 |
+
<button onClick={() => setSidebarOpen(true)} className="lg:hidden p-2 rounded-lg hover:bg-surface-2 text-muted"><Icon name="menu" size={20} /></button>
|
| 135 |
+
<div className="hidden sm:flex items-center gap-2 px-4 py-2 rounded-xl bg-surface-2 border border-border text-muted text-sm w-72">
|
| 136 |
+
<Icon name="search" size={16} />
|
| 137 |
+
<span>Search anything...</span>
|
| 138 |
+
</div>
|
| 139 |
+
</div>
|
| 140 |
+
<div className="flex items-center gap-3">
|
| 141 |
+
<button className="p-2 rounded-lg hover:bg-surface-2 text-muted hover:text-fg transition-colors relative">
|
| 142 |
+
<Icon name="bell" size={20} />
|
| 143 |
+
<span className="absolute top-1.5 right-1.5 w-2 h-2 rounded-full bg-primary" />
|
| 144 |
+
</button>
|
| 145 |
+
<a href="generator.html" className="px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-semibold hover:brightness-110 transition-all">+ New</a>
|
| 146 |
+
<a href="profile.html" className="w-9 h-9 rounded-lg overflow-hidden border border-border">
|
| 147 |
+
<img src="http://static.photos/people/100x100/10" alt="" className="w-full h-full object-cover" />
|
| 148 |
+
</a>
|
| 149 |
+
</div>
|
| 150 |
+
</div>
|
| 151 |
+
</header>
|
| 152 |
+
|
| 153 |
+
<main className="p-4 sm:p-6 lg:p-8">
|
| 154 |
+
<div className="mb-8">
|
| 155 |
+
<h1 className="text-2xl sm:text-3xl font-bold mb-1">Welcome back, <span className="gradient-text">Neon</span></h1>
|
| 156 |
+
<p className="text-muted">Here's what's happening with your creations today.</p>
|
| 157 |
+
</div>
|
| 158 |
+
|
| 159 |
+
{/* Stats */}
|
| 160 |
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
| 161 |
+
{stats.map((s, i) => (
|
| 162 |
+
<div key={i} className="rounded-2xl p-5 bg-surface border border-border hover:border-surface-3 transition-colors">
|
| 163 |
+
<div className="flex items-center justify-between mb-3">
|
| 164 |
+
<div className={`w-10 h-10 rounded-xl ${s.bg} flex items-center justify-center`}><Icon name={s.icon} size={18} className={s.color} /></div>
|
| 165 |
+
<span className="text-xs font-medium text-success bg-success/10 px-2 py-0.5 rounded-full">{s.change}</span>
|
| 166 |
+
</div>
|
| 167 |
+
<div className="text-2xl font-bold text-fg tabular-nums">{s.value}</div>
|
| 168 |
+
<div className="text-xs text-muted mt-1">{s.label}</div>
|
| 169 |
+
</div>
|
| 170 |
+
))}
|
| 171 |
+
</div>
|
| 172 |
+
|
| 173 |
+
<div className="grid lg:grid-cols-3 gap-6">
|
| 174 |
+
{/* Recent Generations */}
|
| 175 |
+
<div className="lg:col-span-2 rounded-2xl p-6 bg-surface border border-border">
|
| 176 |
+
<div className="flex items-center justify-between mb-5">
|
| 177 |
+
<h2 className="text-lg font-semibold">Recent Generations</h2>
|
| 178 |
+
<a href="generator.html" className="text-sm text-primary hover:underline flex items-center gap-1">View all <Icon name="arrow-right" size={14} /></a>
|
| 179 |
+
</div>
|
| 180 |
+
<div className="space-y-3">
|
| 181 |
+
{recentGens.map((g, i) => (
|
| 182 |
+
<div key={i} className="flex items-center gap-4 p-3 rounded-xl hover:bg-surface-2/50 transition-colors cursor-pointer group">
|
| 183 |
+
<img src={g.img} alt="" className="w-12 h-12 rounded-xl object-cover border border-border" />
|
| 184 |
+
<div className="flex-1 min-w-0">
|
| 185 |
+
<div className="text-sm font-medium text-fg truncate">{g.title}</div>
|
| 186 |
+
<div className="text-xs text-muted">{g.model} · {g.time}</div>
|
| 187 |
+
</div>
|
| 188 |
+
<span className={`px-2.5 py-1 rounded-full text-xs font-semibold ${g.status === 'completed' ? 'bg-success/10 text-success border border-success/20' : 'bg-primary/10 text-primary border border-primary/20'}`}>
|
| 189 |
+
{g.status}
|
| 190 |
+
</span>
|
| 191 |
+
</div>
|
| 192 |
+
))}
|
| 193 |
+
</div>
|
| 194 |
+
</div>
|
| 195 |
+
|
| 196 |
+
{/* Right column */}
|
| 197 |
+
<div className="space-y-6">
|
| 198 |
+
{/* Activity */}
|
| 199 |
+
<div className="rounded-2xl p-6 bg-surface border border-border">
|
| 200 |
+
<h2 className="text-lg font-semibold mb-4">Activity</h2>
|
| 201 |
+
<div className="space-y-4">
|
| 202 |
+
{activity.map((a, i) => (
|
| 203 |
+
<div key={i} className="flex items-start gap-3">
|
| 204 |
+
<div className={`mt-0.5 ${a.color}`}><Icon name={a.icon} size={16} /></div>
|
| 205 |
+
<div className="flex-1 min-w-0">
|
| 206 |
+
<div className="text-sm text-fg/90">{a.text}</div>
|
| 207 |
+
<div className="text-xs text-muted mt-0.5">{a.time}</div>
|
| 208 |
+
</div>
|
| 209 |
+
</div>
|
| 210 |
+
))}
|
| 211 |
+
</div>
|
| 212 |
+
</div>
|
| 213 |
+
|
| 214 |
+
{/* Top Models */}
|
| 215 |
+
<div className="rounded-2xl p-6 bg-surface border border-border">
|
| 216 |
+
<div className="flex items-center justify-between mb-4">
|
| 217 |
+
<h2 className="text-lg font-semibold">Top Models</h2>
|
| 218 |
+
<a href="models.html" className="text-xs text-primary hover:underline">See all</a>
|
| 219 |
+
</div>
|
| 220 |
+
<div className="space-y-3">
|
| 221 |
+
{topModels.map((m, i) => (
|
| 222 |
+
<div key={i} className="flex items-center justify-between p-3 rounded-xl hover:bg-surface-2/50 transition-colors cursor-pointer">
|
| 223 |
+
<div className="flex items-center gap-3">
|
| 224 |
+
<span className="w-6 h-6 rounded-full bg-surface-2 flex items-center justify-center text-xs font-bold text-muted">{i+1}</span>
|
| 225 |
+
<div>
|
| 226 |
+
<div className="text-sm font-medium text-fg">{m.name}</div>
|
| 227 |
+
<div className="text-xs text-muted">{m.runs} runs</div>
|
| 228 |
+
</div>
|
| 229 |
+
</div>
|
| 230 |
+
<div className="text-right">
|
| 231 |
+
<div className="text-sm font-semibold text-fg">{m.revenue}</div>
|
| 232 |
+
<div className="text-xs text-success">{m.trend}</div>
|
| 233 |
+
</div>
|
| 234 |
+
</div>
|
| 235 |
+
))}
|
| 236 |
+
</div>
|
| 237 |
+
</div>
|
| 238 |
+
</div>
|
| 239 |
+
</div>
|
| 240 |
+
|
| 241 |
+
{/* Quick Actions */}
|
| 242 |
+
<div className="mt-8 grid grid-cols-2 sm:grid-cols-4 gap-3">
|
| 243 |
+
{[
|
| 244 |
+
{ label: 'Generate Image', icon: 'image', color: 'text-primary', bg: 'bg-primary/10', href: 'generator.html' },
|
| 245 |
+
{ label: 'Create Workflow', icon: 'workflow', color: 'text-secondary', bg: 'bg-secondary/10', href: 'workflows.html' },
|
| 246 |
+
{ label: 'Train Model', icon: 'box', color: 'text-success', bg: 'bg-success/10', href: 'models.html' },
|
| 247 |
+
{ label: 'Explore Marketplace', icon: 'shopping-bag', color: 'text-accent', bg: 'bg-accent/10', href: 'marketplace.html' },
|
| 248 |
+
].map((a, i) => (
|
| 249 |
+
<a key={i} href={a.href} className="group flex flex-col items-center gap-3 p-5 rounded-2xl bg-surface border border-border hover:border-surface-3 transition-all hover:-translate-y-0.5">
|
| 250 |
+
<div className={`w-12 h-12 rounded-xl ${a.bg} flex items-center justify-center group-hover:scale-110 transition-transform`}><Icon name={a.icon} size={22} className={a.color} /></div>
|
| 251 |
+
<span className="text-sm font-medium text-fg">{a.label}</span>
|
| 252 |
+
</a>
|
| 253 |
+
))}
|
| 254 |
+
</div>
|
| 255 |
+
</main>
|
| 256 |
+
</div>
|
| 257 |
+
</div>
|
| 258 |
+
);
|
| 259 |
+
};
|
| 260 |
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
| 261 |
+
</script>
|
| 262 |
+
</body>
|
| 263 |
+
</html>
|
generator.html
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Generator — NSFWStudio</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
| 9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| 10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 15 |
+
<script>
|
| 16 |
+
tailwind.config = { theme: { extend: { fontFamily: { sans: ['Inter','system-ui','sans-serif'], mono: ['JetBrains Mono','monospace'] }, colors: { bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)', fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)', secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)', destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)' } } } }
|
| 17 |
+
</script>
|
| 18 |
+
<style>
|
| 19 |
+
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
| 20 |
+
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
|
| 21 |
+
::selection { background: oklch(0.70 0.28 350 / 0.3); }
|
| 22 |
+
body::after { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat:repeat; background-size:256px 256px; }
|
| 23 |
+
.no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
| 24 |
+
</style>
|
| 25 |
+
</head>
|
| 26 |
+
<body class="antialiased">
|
| 27 |
+
<div id="root"></div>
|
| 28 |
+
<script type="text/babel">
|
| 29 |
+
const { useState } = React;
|
| 30 |
+
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
|
| 31 |
+
const ref = React.useRef(null);
|
| 32 |
+
React.useEffect(() => { if (ref.current) { const existing = ref.current.querySelector('svg'); if (existing) existing.remove(); const svg = lucide.createElement(lucide.icons[name]); if (svg) { svg.setAttribute('width',size); svg.setAttribute('height',size); svg.setAttribute('stroke-width',strokeWidth); ref.current.appendChild(svg); } } }, [name, size, className, strokeWidth]);
|
| 33 |
+
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
|
| 34 |
+
};
|
| 35 |
+
|
| 36 |
+
const navItems = [
|
| 37 |
+
{ name: 'Dashboard', icon: 'layout-dashboard', href: 'dashboard.html' },
|
| 38 |
+
{ name: 'Generator', icon: 'sparkles', href: 'generator.html', active: true },
|
| 39 |
+
{ name: 'Playground', icon: 'code', href: 'playground.html' },
|
| 40 |
+
{ name: 'Models', icon: 'box', href: 'models.html' },
|
| 41 |
+
{ name: 'Workflows', icon: 'workflow', href: 'workflows.html' },
|
| 42 |
+
{ name: 'Marketplace', icon: 'shopping-bag', href: 'marketplace.html' },
|
| 43 |
+
{ name: 'API Keys', icon: 'key', href: 'api-keys.html' },
|
| 44 |
+
{ divider: true },
|
| 45 |
+
{ name: 'Profile', icon: 'user', href: 'profile.html' },
|
| 46 |
+
{ name: 'Settings', icon: 'settings', href: 'settings.html' },
|
| 47 |
+
];
|
| 48 |
+
|
| 49 |
+
const Sidebar = ({ isOpen, setIsOpen }) => (
|
| 50 |
+
<>
|
| 51 |
+
<div className={`fixed inset-0 z-40 lg:hidden transition-all ${isOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
|
| 52 |
+
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setIsOpen(false)} />
|
| 53 |
+
</div>
|
| 54 |
+
<aside className={`fixed top-0 left-0 bottom-0 z-50 w-64 bg-surface border-r border-border flex flex-col transition-transform duration-300 lg:translate-x-0 ${isOpen ? 'translate-x-0' : '-translate-x-full'}`}>
|
| 55 |
+
<div className="p-4 flex items-center gap-2.5 border-b border-border"><div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div><span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span></div>
|
| 56 |
+
<nav className="flex-1 p-3 space-y-1 overflow-y-auto no-scrollbar">{navItems.map((item, i) => item.divider ? <div key={i} className="my-3 border-t border-border" /> : <a key={i} href={item.href} className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors ${item.active ? 'bg-primary/10 text-primary border border-primary/20' : 'text-muted hover:text-fg hover:bg-surface-2 border border-transparent'}`}><Icon name={item.icon} size={18} />{item.name}</a>)}</nav>
|
| 57 |
+
<div className="p-4 border-t border-border"><a href="documentation.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="book-open" size={18} />Documentation</a><a href="api-reference.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="file-code" size={18} />API Reference</a></div>
|
| 58 |
+
</aside>
|
| 59 |
+
</>
|
| 60 |
+
);
|
| 61 |
+
|
| 62 |
+
const App = () => {
|
| 63 |
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
| 64 |
+
const [model, setModel] = useState('DreamVision XL');
|
| 65 |
+
const [prompt, setPrompt] = useState('');
|
| 66 |
+
const [negativePrompt, setNegativePrompt] = useState('');
|
| 67 |
+
const [isGenerating, setIsGenerating] = useState(false);
|
| 68 |
+
const [generated, setGenerated] = useState(false);
|
| 69 |
+
const [aspectRatio, setAspectRatio] = useState('1:1');
|
| 70 |
+
|
| 71 |
+
const handleGenerate = () => { if (!prompt.trim()) return; setIsGenerating(true); setGenerated(false); setTimeout(() => { setIsGenerating(false); setGenerated(true); }, 2500); };
|
| 72 |
+
|
| 73 |
+
return (
|
| 74 |
+
<div className="min-h-screen bg-bg">
|
| 75 |
+
<Sidebar isOpen={sidebarOpen} setIsOpen={setSidebarOpen} />
|
| 76 |
+
<div className="lg:ml-64">
|
| 77 |
+
<header className="sticky top-0 z-30 glass border-b border-border">
|
| 78 |
+
<div className="flex items-center justify-between px-4 sm:px-6 h-16">
|
| 79 |
+
<div className="flex items-center gap-4">
|
| 80 |
+
<button onClick={() => setSidebarOpen(true)} className="lg:hidden p-2 rounded-lg hover:bg-surface-2 text-muted"><Icon name="menu" size={20} /></button>
|
| 81 |
+
<h1 className="text-xl font-bold">Generator</h1>
|
| 82 |
+
</div>
|
| 83 |
+
<div className="flex items-center gap-3">
|
| 84 |
+
<button className="p-2 rounded-lg hover:bg-surface-2 text-muted relative"><Icon name="bell" size={20} /><span className="absolute top-1.5 right-1.5 w-2 h-2 rounded-full bg-primary" /></button>
|
| 85 |
+
<a href="profile.html" className="w-9 h-9 rounded-lg overflow-hidden border border-border"><img src="http://static.photos/people/100x100/10" alt="" className="w-full h-full object-cover" /></a>
|
| 86 |
+
</div>
|
| 87 |
+
</div>
|
| 88 |
+
</header>
|
| 89 |
+
|
| 90 |
+
<div className="flex flex-col lg:flex-row h-[calc(100vh-64px)]">
|
| 91 |
+
{/* Settings Panel */}
|
| 92 |
+
<div className="w-full lg:w-80 border-b lg:border-b-0 lg:border-r border-border overflow-y-auto no-scrollbar p-4 space-y-5">
|
| 93 |
+
<div>
|
| 94 |
+
<label className="text-xs font-medium text-muted uppercase tracking-wider mb-2 block">Model</label>
|
| 95 |
+
<select value={model} onChange={e => setModel(e.target.value)} className="w-full px-3 py-2.5 rounded-xl bg-surface-2 border border-border text-sm text-fg focus:outline-none focus:ring-2 focus:ring-primary/50">
|
| 96 |
+
{['DreamVision XL', 'SDXL 1.0', 'CyberPunk v3', 'Anime Diffusion'].map(m => <option key={m}>{m}</option>)}
|
| 97 |
+
</select>
|
| 98 |
+
</div>
|
| 99 |
+
<div>
|
| 100 |
+
<label className="text-xs font-medium text-muted uppercase tracking-wider mb-2 block">Aspect Ratio</label>
|
| 101 |
+
<div className="grid grid-cols-4 gap-2">
|
| 102 |
+
{['1:1', '4:3', '16:9', '9:16'].map(r => (
|
| 103 |
+
<button key={r} onClick={() => setAspectRatio(r)} className={`py-2 rounded-lg text-xs font-medium transition-colors ${aspectRatio === r ? 'bg-primary text-primary-foreground' : 'bg-surface-2 border border-border text-muted hover:text-fg'}`}>{r}</button>
|
| 104 |
+
))}
|
| 105 |
+
</div>
|
| 106 |
+
</div>
|
| 107 |
+
{[
|
| 108 |
+
{ label: 'Steps', val: '30', desc: 'Number of denoising steps' },
|
| 109 |
+
].map(p => (
|
| 110 |
+
<div key={p.label}>
|
| 111 |
+
<label className="text-xs font-medium text-muted uppercase tracking-wider mb-2 block">{p.label}: {p.val}</label>
|
| 112 |
+
<input type="range" min="1" max="50" defaultValue="30" className="w-full accent-primary" />
|
| 113 |
+
<p className="text-xs text-muted mt-1">{p.desc}</p>
|
| 114 |
+
</div>
|
| 115 |
+
))}
|
| 116 |
+
<div>
|
| 117 |
+
<label className="text-xs font-medium text-muted uppercase tracking-wider mb-2 block">Guidance Scale: 7.5</label>
|
| 118 |
+
<input type="range" min="1" max="20" defaultValue="7.5" step="0.5" className="w-full accent-primary" />
|
| 119 |
+
</div>
|
| 120 |
+
<div>
|
| 121 |
+
<label className="text-xs font-medium text-muted uppercase tracking-wider mb-2 block">Seed</label>
|
| 122 |
+
<div className="flex gap-2">
|
| 123 |
+
<input type="text" defaultValue="Random" className="flex-1 px-3 py-2.5 rounded-xl bg-surface-2 border border-border text-sm text-fg focus:outline-none focus:ring-2 focus:ring-primary/50" />
|
| 124 |
+
<button className="px-3 rounded-xl bg-surface-2 border border-border text-muted hover:text-fg"><Icon name="shuffle" size={16} /></button>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
<div className="pt-2 border-t border-border">
|
| 128 |
+
<h3 className="text-xs font-medium text-muted uppercase tracking-wider mb-2">Quick Styles</h3>
|
| 129 |
+
<div className="flex flex-wrap gap-2">
|
| 130 |
+
{['Photorealistic', 'Anime', 'Fantasy', 'Cinematic', 'Oil Painting', '3D Render'].map(s => (
|
| 131 |
+
<button key={s} className="px-3 py-1.5 rounded-lg bg-surface-2 border border-border text-xs text-muted hover:text-fg hover:border-surface-3 transition-colors">{s}</button>
|
| 132 |
+
))}
|
| 133 |
+
</div>
|
| 134 |
+
</div>
|
| 135 |
+
</div>
|
| 136 |
+
|
| 137 |
+
{/* Main Canvas */}
|
| 138 |
+
<div className="flex-1 flex flex-col">
|
| 139 |
+
<div className="flex-1 flex items-center justify-center p-4 sm:p-8">
|
| 140 |
+
{isGenerating ? (
|
| 141 |
+
<div className="text-center">
|
| 142 |
+
<div className="w-16 h-16 border-4 border-primary border-t-transparent rounded-full animate-spin mx-auto mb-4" />
|
| 143 |
+
<p className="text-fg font-medium">Generating your masterpiece...</p>
|
| 144 |
+
<p className="text-sm text-muted mt-1">This usually takes 10-30 seconds</p>
|
| 145 |
+
</div>
|
| 146 |
+
) : generated ? (
|
| 147 |
+
<div className="max-w-2xl w-full">
|
| 148 |
+
<div className="rounded-2xl overflow-hidden border border-border shadow-2xl shadow-black/40">
|
| 149 |
+
<img src={`http://static.photos/cyberpunk/1024x576/1`} alt="Generated" className="w-full" />
|
| 150 |
+
</div>
|
| 151 |
+
<div className="flex items-center justify-between mt-4">
|
| 152 |
+
<div className="flex gap-2">
|
| 153 |
+
{['Download', 'Upscale', 'Variations', 'Add to Collection'].map(a => (
|
| 154 |
+
<button key={a} className="px-4 py-2 rounded-lg bg-surface border border-border text-sm text-fg hover:bg-surface-2 transition-colors flex items-center gap-1.5"><Icon name={a === 'Download' ? 'download' : a === 'Upscale' ? 'maximize' : a === 'Variations' ? 'copy' : 'bookmark'} size={14} />{a}</button>
|
| 155 |
+
))}
|
| 156 |
+
</div>
|
| 157 |
+
<span className="text-xs text-muted">DreamVision XL · 1024×1024 · 30 steps</span>
|
| 158 |
+
</div>
|
| 159 |
+
</div>
|
| 160 |
+
) : (
|
| 161 |
+
<div className="text-center max-w-md">
|
| 162 |
+
<div className="w-20 h-20 rounded-2xl bg-surface-2 flex items-center justify-center mx-auto mb-6"><Icon name="sparkles" size={32} className="text-muted" /></div>
|
| 163 |
+
<h2 className="text-xl font-semibold mb-2">Ready to Create</h2>
|
| 164 |
+
<p className="text-muted text-sm">Enter a prompt below and click Generate to bring your vision to life.</p>
|
| 165 |
+
</div>
|
| 166 |
+
)}
|
| 167 |
+
</div>
|
| 168 |
+
|
| 169 |
+
{/* Prompt Input */}
|
| 170 |
+
<div className="p-4 border-t border-border">
|
| 171 |
+
<div className="max-w-4xl mx-auto space-y-3">
|
| 172 |
+
<textarea value={negativePrompt} onChange={e => setNegativePrompt(e.target.value)} placeholder="Negative prompt (optional)..." className="w-full px-4 py-2.5 rounded-xl bg-surface border border-border text-sm text-fg placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 resize-none h-16" />
|
| 173 |
+
<div className="flex gap-3">
|
| 174 |
+
<div className="flex-1 flex items-center gap-2 px-4 py-3 rounded-xl bg-surface border border-border focus-within:ring-2 focus-within:ring-primary/50">
|
| 175 |
+
<Icon name="sparkles" size={18} className="text-muted shrink-0" />
|
| 176 |
+
<input type="text" value={prompt} onChange={e => setPrompt(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleGenerate()} placeholder="Describe what you want to create..." className="flex-1 bg-transparent text-fg placeholder:text-muted outline-none text-sm" />
|
| 177 |
+
</div>
|
| 178 |
+
<button onClick={handleGenerate} disabled={isGenerating} className="px-6 py-3 rounded-xl bg-primary text-primary-foreground font-semibold hover:brightness-110 transition-all shadow-lg shadow-primary/25 flex items-center gap-2 shrink-0">
|
| 179 |
+
<Icon name="zap" size={18} />Generate
|
| 180 |
+
</button>
|
| 181 |
+
</div>
|
| 182 |
+
</div>
|
| 183 |
+
</div>
|
| 184 |
+
</div>
|
| 185 |
+
</div>
|
| 186 |
+
</div>
|
| 187 |
+
</div>
|
| 188 |
+
);
|
| 189 |
+
};
|
| 190 |
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
| 191 |
+
</script>
|
| 192 |
+
</body>
|
| 193 |
+
</html>
|
index.html
CHANGED
|
@@ -1,19 +1,826 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en" class="scroll-smooth">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>NSFWStudio — The AI Creation Platform</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
| 9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| 10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 15 |
+
<script>
|
| 16 |
+
tailwind.config = {
|
| 17 |
+
theme: {
|
| 18 |
+
extend: {
|
| 19 |
+
fontFamily: { sans: ['Inter', 'system-ui', 'sans-serif'], mono: ['JetBrains Mono', 'monospace'] },
|
| 20 |
+
colors: {
|
| 21 |
+
bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)',
|
| 22 |
+
fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)',
|
| 23 |
+
secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)',
|
| 24 |
+
destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)',
|
| 25 |
+
},
|
| 26 |
+
keyframes: {
|
| 27 |
+
'fade-up': { 'from': { opacity: '0', transform: 'translateY(20px)' }, 'to': { opacity: '1', transform: 'translateY(0)' } },
|
| 28 |
+
'fade-in': { 'from': { opacity: '0' }, 'to': { opacity: '1' } },
|
| 29 |
+
'marquee': { 'from': { transform: 'translateX(0)' }, 'to': { transform: 'translateX(-50%)' } },
|
| 30 |
+
'pulse-glow': { '0%, 100%': { opacity: '0.4' }, '50%': { opacity: '0.8' } },
|
| 31 |
+
shimmer: { '0%': { backgroundPosition: '-200% 0' }, '100%': { backgroundPosition: '200% 0' } },
|
| 32 |
+
},
|
| 33 |
+
animation: {
|
| 34 |
+
'fade-up': 'fade-up 0.6s ease-out forwards',
|
| 35 |
+
'fade-in': 'fade-in 0.5s ease-out forwards',
|
| 36 |
+
'marquee': 'marquee 30s linear infinite',
|
| 37 |
+
'pulse-glow': 'pulse-glow 3s ease-in-out infinite',
|
| 38 |
+
shimmer: 'shimmer 2s linear infinite',
|
| 39 |
+
},
|
| 40 |
+
},
|
| 41 |
+
},
|
| 42 |
+
}
|
| 43 |
+
</script>
|
| 44 |
+
<style>
|
| 45 |
+
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
| 46 |
+
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
|
| 47 |
+
.glass-strong { background: oklch(0.14 0.015 285 / 0.85); backdrop-filter: blur(32px) saturate(1.4); -webkit-backdrop-filter: blur(32px) saturate(1.4); border: 1px solid oklch(0.25 0.02 285 / 0.6); }
|
| 48 |
+
.gradient-text { background: linear-gradient(135deg, oklch(0.70 0.28 350), oklch(0.75 0.15 215)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
| 49 |
+
.gradient-text-warm { background: linear-gradient(135deg, oklch(0.70 0.28 350), oklch(0.75 0.18 60)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
| 50 |
+
.grid-pattern { background-image: linear-gradient(oklch(0.22 0.02 285 / 0.3) 1px, transparent 1px), linear-gradient(90deg, oklch(0.22 0.02 285 / 0.3) 1px, transparent 1px); background-size: 64px 64px; }
|
| 51 |
+
.dot-pattern { background-image: radial-gradient(oklch(0.25 0.02 285 / 0.4) 1px, transparent 1px); background-size: 24px 24px; }
|
| 52 |
+
.reveal { opacity: 0; transform: translateY(24px); transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1); }
|
| 53 |
+
.reveal.visible { opacity: 1; transform: translateY(0); }
|
| 54 |
+
.reveal-delay-1 { transition-delay: 0.1s; }
|
| 55 |
+
.reveal-delay-2 { transition-delay: 0.2s; }
|
| 56 |
+
.reveal-delay-3 { transition-delay: 0.3s; }
|
| 57 |
+
.reveal-delay-4 { transition-delay: 0.4s; }
|
| 58 |
+
.reveal-delay-5 { transition-delay: 0.5s; }
|
| 59 |
+
.typing-cursor::after { content: '|'; animation: blink 1s step-end infinite; color: oklch(0.70 0.28 350); font-weight: 300; }
|
| 60 |
+
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
|
| 61 |
+
@keyframes float { 0%, 100% { transform: translateY(0px); } 50% { transform: translateY(-8px); } }
|
| 62 |
+
.animate-float { animation: float 4s ease-in-out infinite; }
|
| 63 |
+
.animate-float-delay { animation: float 4s ease-in-out 1s infinite; }
|
| 64 |
+
.animate-float-slow { animation: float 6s ease-in-out infinite; }
|
| 65 |
+
.card-highlight { position: relative; }
|
| 66 |
+
.card-highlight::before { content: ''; position: absolute; inset: -1px; border-radius: inherit; background: linear-gradient(135deg, oklch(0.70 0.28 350 / 0.4), oklch(0.75 0.15 215 / 0.1), transparent, transparent); z-index: -1; opacity: 0; transition: opacity 0.3s ease; }
|
| 67 |
+
.card-highlight:hover::before { opacity: 1; }
|
| 68 |
+
.card-tilt { transition: transform 0.3s ease, box-shadow 0.3s ease; }
|
| 69 |
+
.card-tilt:hover { transform: translateY(-4px); box-shadow: 0 20px 40px rgba(0,0,0,0.3), 0 0 0 1px oklch(0.25 0.02 285 / 0.5); }
|
| 70 |
+
.tabular-nums { font-variant-numeric: tabular-nums; }
|
| 71 |
+
::selection { background: oklch(0.70 0.28 350 / 0.3); color: white; }
|
| 72 |
+
body::after { content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 9999; opacity: 0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat: repeat; background-size: 256px 256px; }
|
| 73 |
+
.scroll-progress { position: fixed; top: 0; left: 0; height: 3px; background: linear-gradient(90deg, oklch(0.70 0.28 350), oklch(0.75 0.15 215)); z-index: 100; transition: width 0.1s linear; box-shadow: 0 0 10px oklch(0.70 0.28 350 / 0.5); }
|
| 74 |
+
.faq-content { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.35s ease-out; }
|
| 75 |
+
.faq-content.open { grid-template-rows: 1fr; }
|
| 76 |
+
.faq-content > div { overflow: hidden; }
|
| 77 |
+
.stat-number { background: linear-gradient(135deg, oklch(0.97 0.005 285), oklch(0.70 0.28 350)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
| 78 |
+
.no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
| 79 |
+
</style>
|
| 80 |
+
</head>
|
| 81 |
+
<body class="antialiased overflow-x-hidden">
|
| 82 |
+
<div id="root"></div>
|
| 83 |
+
|
| 84 |
+
<script type="text/babel">
|
| 85 |
+
const { useState, useEffect, useRef, useCallback } = React;
|
| 86 |
+
|
| 87 |
+
const useScrollReveal = () => {
|
| 88 |
+
useEffect(() => {
|
| 89 |
+
const observer = new IntersectionObserver(
|
| 90 |
+
(entries) => { entries.forEach(entry => { if (entry.isIntersecting) entry.target.classList.add('visible'); }); },
|
| 91 |
+
{ threshold: 0.1, rootMargin: '0px 0px -40px 0px' }
|
| 92 |
+
);
|
| 93 |
+
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
|
| 94 |
+
return () => observer.disconnect();
|
| 95 |
+
}, []);
|
| 96 |
+
};
|
| 97 |
+
|
| 98 |
+
const useCountUp = (end, duration = 2000) => {
|
| 99 |
+
const [count, setCount] = useState(0);
|
| 100 |
+
const ref = useRef(null);
|
| 101 |
+
const isInView = useRef(false);
|
| 102 |
+
useEffect(() => {
|
| 103 |
+
const observer = new IntersectionObserver(([entry]) => {
|
| 104 |
+
if (entry.isIntersecting && !isInView.current) {
|
| 105 |
+
isInView.current = true;
|
| 106 |
+
const startTime = performance.now();
|
| 107 |
+
const animate = (now) => {
|
| 108 |
+
const elapsed = now - startTime;
|
| 109 |
+
const progress = Math.min(elapsed / duration, 1);
|
| 110 |
+
const eased = 1 - Math.pow(1 - progress, 3);
|
| 111 |
+
setCount(Math.floor(eased * end));
|
| 112 |
+
if (progress < 1) requestAnimationFrame(animate);
|
| 113 |
+
};
|
| 114 |
+
requestAnimationFrame(animate);
|
| 115 |
+
}
|
| 116 |
+
}, { threshold: 0.5 });
|
| 117 |
+
if (ref.current) observer.observe(ref.current);
|
| 118 |
+
return () => observer.disconnect();
|
| 119 |
+
}, [end, duration]);
|
| 120 |
+
return [count, ref];
|
| 121 |
+
};
|
| 122 |
+
|
| 123 |
+
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
|
| 124 |
+
const ref = useRef(null);
|
| 125 |
+
useEffect(() => {
|
| 126 |
+
if (ref.current) {
|
| 127 |
+
const existing = ref.current.querySelector('svg');
|
| 128 |
+
if (existing) existing.remove();
|
| 129 |
+
const svg = lucide.createElement(lucide.icons[name]);
|
| 130 |
+
if (svg) {
|
| 131 |
+
svg.setAttribute('width', size);
|
| 132 |
+
svg.setAttribute('height', size);
|
| 133 |
+
svg.setAttribute('stroke-width', strokeWidth);
|
| 134 |
+
ref.current.appendChild(svg);
|
| 135 |
+
}
|
| 136 |
+
}
|
| 137 |
+
}, [name, size, className, strokeWidth]);
|
| 138 |
+
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
|
| 139 |
+
};
|
| 140 |
+
|
| 141 |
+
const TopBar = () => (
|
| 142 |
+
<div className="fixed top-0 left-0 right-0 z-[60] h-8 bg-surface border-b border-border flex items-center justify-center px-4">
|
| 143 |
+
<div className="flex items-center gap-6 text-xs text-muted">
|
| 144 |
+
<a href="dashboard.html" className="hover:text-fg transition-colors">Dashboard</a>
|
| 145 |
+
<a href="generator.html" className="hover:text-fg transition-colors">Generator</a>
|
| 146 |
+
<a href="models.html" className="hover:text-fg transition-colors">Models</a>
|
| 147 |
+
<a href="marketplace.html" className="hover:text-fg transition-colors">Marketplace</a>
|
| 148 |
+
<a href="api-keys.html" className="hover:text-fg transition-colors">API</a>
|
| 149 |
+
<a href="signin.html" className="text-primary font-medium hover:underline">Sign In</a>
|
| 150 |
+
</div>
|
| 151 |
+
</div>
|
| 152 |
+
);
|
| 153 |
+
|
| 154 |
+
const ScrollProgress = () => {
|
| 155 |
+
const [progress, setProgress] = useState(0);
|
| 156 |
+
useEffect(() => {
|
| 157 |
+
const onScroll = () => {
|
| 158 |
+
const scrollTop = window.scrollY;
|
| 159 |
+
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
| 160 |
+
setProgress(docHeight > 0 ? (scrollTop / docHeight) * 100 : 0);
|
| 161 |
+
};
|
| 162 |
+
window.addEventListener('scroll', onScroll, { passive: true });
|
| 163 |
+
return () => window.removeEventListener('scroll', onScroll);
|
| 164 |
+
}, []);
|
| 165 |
+
return <div className="scroll-progress" style={{ width: `${progress}%` }} />;
|
| 166 |
+
};
|
| 167 |
+
|
| 168 |
+
const BackToTop = () => {
|
| 169 |
+
const [visible, setVisible] = useState(false);
|
| 170 |
+
useEffect(() => {
|
| 171 |
+
const onScroll = () => setVisible(window.scrollY > 600);
|
| 172 |
+
window.addEventListener('scroll', onScroll, { passive: true });
|
| 173 |
+
return () => window.removeEventListener('scroll', onScroll);
|
| 174 |
+
}, []);
|
| 175 |
+
return (
|
| 176 |
+
<button
|
| 177 |
+
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
| 178 |
+
className={`fixed bottom-6 right-6 z-50 w-12 h-12 rounded-xl bg-primary text-primary-foreground flex items-center justify-center shadow-lg shadow-primary/25 hover:brightness-110 transition-all duration-300 ${visible ? 'opacity-100 translate-y-0 pointer-events-auto' : 'opacity-0 translate-y-4 pointer-events-none'}`}
|
| 179 |
+
>
|
| 180 |
+
<Icon name="arrow-up" size={20} />
|
| 181 |
+
</button>
|
| 182 |
+
);
|
| 183 |
+
};
|
| 184 |
+
|
| 185 |
+
const Navbar = () => {
|
| 186 |
+
const [isScrolled, setIsScrolled] = useState(false);
|
| 187 |
+
const [mobileOpen, setMobileOpen] = useState(false);
|
| 188 |
+
useEffect(() => {
|
| 189 |
+
const onScroll = () => setIsScrolled(window.scrollY > 40);
|
| 190 |
+
window.addEventListener('scroll', onScroll, { passive: true });
|
| 191 |
+
return () => window.removeEventListener('scroll', onScroll);
|
| 192 |
+
}, []);
|
| 193 |
+
useEffect(() => {
|
| 194 |
+
document.body.style.overflow = mobileOpen ? 'hidden' : '';
|
| 195 |
+
return () => { document.body.style.overflow = ''; };
|
| 196 |
+
}, [mobileOpen]);
|
| 197 |
+
const links = [
|
| 198 |
+
{ name: 'Studio', href: 'generator.html' },
|
| 199 |
+
{ name: 'Models', href: 'models.html' },
|
| 200 |
+
{ name: 'Agents', href: 'dashboard.html' },
|
| 201 |
+
{ name: 'Workflows', href: 'workflows.html' },
|
| 202 |
+
{ name: 'Marketplace', href: 'marketplace.html' },
|
| 203 |
+
{ name: 'Pricing', href: '#pricing' },
|
| 204 |
+
{ name: 'Docs', href: 'playground.html' },
|
| 205 |
+
];
|
| 206 |
+
return (
|
| 207 |
+
<>
|
| 208 |
+
<nav className={`fixed top-8 w-full z-50 transition-all duration-300 ${isScrolled ? 'glass-strong shadow-lg shadow-black/20' : 'bg-transparent'}`}>
|
| 209 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 210 |
+
<div className="flex items-center justify-between h-16">
|
| 211 |
+
<a href="#" className="flex items-center gap-2.5 group">
|
| 212 |
+
<div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div>
|
| 213 |
+
<span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span>
|
| 214 |
+
</a>
|
| 215 |
+
<div className="hidden lg:flex items-center gap-1">
|
| 216 |
+
{links.map(l => <a key={l.name} href={l.href} className="px-3 py-2 text-sm font-medium text-muted hover:text-fg transition-colors rounded-lg hover:bg-surface-2">{l.name}</a>)}
|
| 217 |
+
</div>
|
| 218 |
+
<div className="hidden lg:flex items-center gap-3">
|
| 219 |
+
<a href="signin.html" className="text-sm font-medium text-muted hover:text-fg transition-colors px-3 py-2">Sign in</a>
|
| 220 |
+
<a href="signin.html" className="px-5 py-2.5 rounded-lg bg-primary text-primary-foreground text-sm font-semibold hover:brightness-110 transition-all shadow-lg shadow-primary/25">Get Started Free</a>
|
| 221 |
+
</div>
|
| 222 |
+
<button onClick={() => setMobileOpen(!mobileOpen)} className="lg:hidden p-2 rounded-lg hover:bg-surface-2 text-muted hover:text-fg transition-colors">
|
| 223 |
+
<Icon name={mobileOpen ? 'x' : 'menu'} size={22} />
|
| 224 |
+
</button>
|
| 225 |
+
</div>
|
| 226 |
+
</div>
|
| 227 |
+
</nav>
|
| 228 |
+
<div className={`fixed inset-0 z-40 lg:hidden transition-all duration-300 ${mobileOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
|
| 229 |
+
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setMobileOpen(false)} />
|
| 230 |
+
<div className={`absolute right-0 top-0 bottom-0 w-80 max-w-[85vw] bg-surface border-l border-border p-6 pt-20 transition-transform duration-300 ${mobileOpen ? 'translate-x-0' : 'translate-x-full'}`}>
|
| 231 |
+
<div className="flex flex-col gap-1">
|
| 232 |
+
{links.map(l => <a key={l.name} href={l.href} onClick={() => setMobileOpen(false)} className="px-4 py-3 text-base font-medium text-muted hover:text-fg hover:bg-surface-2 rounded-lg transition-colors">{l.name}</a>)}
|
| 233 |
+
</div>
|
| 234 |
+
<div className="mt-6 pt-6 border-t border-border flex flex-col gap-3">
|
| 235 |
+
<a href="signin.html" className="px-4 py-3 text-base font-medium text-muted hover:text-fg text-center rounded-lg hover:bg-surface-2 transition-colors">Sign in</a>
|
| 236 |
+
<a href="signin.html" className="px-4 py-3 bg-primary text-primary-foreground font-semibold text-center rounded-lg hover:brightness-110 transition-all">Get Started Free</a>
|
| 237 |
+
</div>
|
| 238 |
+
</div>
|
| 239 |
+
</div>
|
| 240 |
+
</>
|
| 241 |
+
);
|
| 242 |
+
};
|
| 243 |
+
|
| 244 |
+
const Hero = () => {
|
| 245 |
+
const words = ['Images', 'Videos', 'Agents', 'Workflows', 'Models'];
|
| 246 |
+
const [typedWord, setTypedWord] = useState('');
|
| 247 |
+
const [wordIndex, setWordIndex] = useState(0);
|
| 248 |
+
const [isDeleting, setIsDeleting] = useState(false);
|
| 249 |
+
useEffect(() => {
|
| 250 |
+
const currentWord = words[wordIndex];
|
| 251 |
+
let timeout;
|
| 252 |
+
if (!isDeleting) {
|
| 253 |
+
if (typedWord.length < currentWord.length) {
|
| 254 |
+
timeout = setTimeout(() => setTypedWord(currentWord.slice(0, typedWord.length + 1)), 80 + Math.random() * 40);
|
| 255 |
+
} else {
|
| 256 |
+
timeout = setTimeout(() => setIsDeleting(true), 2000);
|
| 257 |
+
}
|
| 258 |
+
} else {
|
| 259 |
+
if (typedWord.length > 0) {
|
| 260 |
+
timeout = setTimeout(() => setTypedWord(typedWord.slice(0, -1)), 40);
|
| 261 |
+
} else {
|
| 262 |
+
setIsDeleting(false);
|
| 263 |
+
setWordIndex((prev) => (prev + 1) % words.length);
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
return () => clearTimeout(timeout);
|
| 267 |
+
}, [typedWord, isDeleting, wordIndex]);
|
| 268 |
+
const stats = [
|
| 269 |
+
{ label: 'Creators', value: '2.8M+', icon: 'users' },
|
| 270 |
+
{ label: 'AI Models', value: '45.8K+', icon: 'box' },
|
| 271 |
+
{ label: 'Workflows', value: '1.2M+', icon: 'workflow' },
|
| 272 |
+
{ label: 'Uptime', value: '99.9%', icon: 'activity' },
|
| 273 |
+
];
|
| 274 |
+
return (
|
| 275 |
+
<section className="relative pt-36 pb-8 lg:pt-44 lg:pb-12 overflow-hidden">
|
| 276 |
+
<div className="absolute inset-0 grid-pattern opacity-40 pointer-events-none" />
|
| 277 |
+
<div className="absolute top-0 right-0 w-[900px] h-[900px] bg-primary/15 rounded-full blur-[160px] -translate-y-1/2 translate-x-1/3 pointer-events-none animate-pulse-glow" />
|
| 278 |
+
<div className="absolute bottom-0 left-0 w-[600px] h-[600px] bg-secondary/10 rounded-full blur-[140px] translate-y-1/3 -translate-x-1/3 pointer-events-none" />
|
| 279 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
| 280 |
+
<div className="grid lg:grid-cols-2 gap-8 lg:gap-16 items-center">
|
| 281 |
+
<div className="space-y-8">
|
| 282 |
+
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full border border-border bg-surface/80 backdrop-blur-sm reveal">
|
| 283 |
+
<span className="w-2 h-2 rounded-full bg-primary animate-pulse" />
|
| 284 |
+
<span className="text-xs font-semibold tracking-wider uppercase text-muted">Now in Public Beta — v3.0</span>
|
| 285 |
+
</div>
|
| 286 |
+
<h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold leading-[1.05] tracking-tight reveal reveal-delay-1">
|
| 287 |
+
Create <span className="gradient-text">Without</span><br />Limits.<br />
|
| 288 |
+
<span className="relative inline-flex min-w-[200px] sm:min-w-[260px]">
|
| 289 |
+
<span className="gradient-text-warm typing-cursor">{typedWord}</span>
|
| 290 |
+
</span>
|
| 291 |
+
</h1>
|
| 292 |
+
<p className="text-base lg:text-lg text-muted max-w-lg leading-relaxed reveal reveal-delay-2">
|
| 293 |
+
The all-in-one platform for AI creators. Build, automate, and monetize with the most powerful AI tools — from text to image to autonomous agents.
|
| 294 |
+
</p>
|
| 295 |
+
<div className="flex flex-wrap gap-3 reveal reveal-delay-3">
|
| 296 |
+
<a href="signin.html" className="group inline-flex items-center gap-2.5 px-7 py-4 rounded-xl bg-primary text-primary-foreground font-semibold hover:brightness-110 transition-all shadow-xl shadow-primary/25 hover:shadow-primary/40">
|
| 297 |
+
Start Creating <Icon name="arrow-right" size={16} className="group-hover:translate-x-0.5 transition-transform" />
|
| 298 |
+
</a>
|
| 299 |
+
<a href="playground.html" className="inline-flex items-center gap-2.5 px-7 py-4 rounded-xl border border-border bg-surface/50 text-fg font-semibold hover:bg-surface-2 hover:border-surface-3 transition-all">
|
| 300 |
+
<Icon name="play" size={16} /> Watch Demo
|
| 301 |
+
</a>
|
| 302 |
+
</div>
|
| 303 |
+
<div className="grid grid-cols-2 sm:grid-cols-4 gap-6 pt-8 border-t border-border reveal reveal-delay-4">
|
| 304 |
+
{stats.map((s, i) => (
|
| 305 |
+
<div key={i} className="group">
|
| 306 |
+
<div className="flex items-center gap-1.5 mb-1.5">
|
| 307 |
+
<Icon name={s.icon} size={14} className="text-muted group-hover:text-primary transition-colors" />
|
| 308 |
+
<span className="text-xs text-muted uppercase tracking-wider font-medium">{s.label}</span>
|
| 309 |
+
</div>
|
| 310 |
+
<div className="text-xl sm:text-2xl font-bold text-fg tabular-nums">{s.value}</div>
|
| 311 |
+
</div>
|
| 312 |
+
))}
|
| 313 |
+
</div>
|
| 314 |
+
</div>
|
| 315 |
+
<div className="relative lg:h-[620px] flex items-center justify-center reveal reveal-delay-2">
|
| 316 |
+
<div className="absolute inset-0 rounded-3xl overflow-hidden opacity-40 pointer-events-none">
|
| 317 |
+
<img src="http://static.photos/cyberpunk/800x1000/1" alt="" className="w-full h-full object-cover" />
|
| 318 |
+
<div className="absolute inset-0 bg-gradient-to-t from-bg via-bg/60 to-transparent" />
|
| 319 |
+
<div className="absolute inset-0 bg-gradient-to-r from-bg/80 to-transparent" />
|
| 320 |
+
</div>
|
| 321 |
+
<div className="relative w-full max-w-md glass rounded-2xl p-5 shadow-2xl shadow-black/40">
|
| 322 |
+
<div className="flex items-center justify-between mb-4">
|
| 323 |
+
<div className="flex items-center gap-2">
|
| 324 |
+
<div className="w-2 h-2 rounded-full bg-success animate-pulse" />
|
| 325 |
+
<span className="text-sm font-medium text-fg">Real-time Generation</span>
|
| 326 |
+
</div>
|
| 327 |
+
<span className="px-2.5 py-1 rounded-md bg-success/10 text-success text-xs font-semibold border border-success/20">LIVE</span>
|
| 328 |
+
</div>
|
| 329 |
+
<div className="relative rounded-xl overflow-hidden mb-4 aspect-video bg-surface-3 group cursor-pointer">
|
| 330 |
+
<img src="http://static.photos/cyberpunk/640x360/2" alt="Generated preview" className="w-full h-full object-cover opacity-90 group-hover:scale-105 transition-transform duration-700" />
|
| 331 |
+
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/30">
|
| 332 |
+
<div className="w-14 h-14 rounded-full bg-white/20 backdrop-blur-sm flex items-center justify-center border border-white/20">
|
| 333 |
+
<Icon name="play" size={24} className="text-white ml-1" />
|
| 334 |
+
</div>
|
| 335 |
+
</div>
|
| 336 |
+
</div>
|
| 337 |
+
<div className="space-y-3">
|
| 338 |
+
<div className="flex justify-between items-end">
|
| 339 |
+
<div>
|
| 340 |
+
<h3 className="font-semibold text-fg text-sm">Cyberpunk Cityscape</h3>
|
| 341 |
+
<p className="text-xs text-muted">4K Ultra — DreamVision XL</p>
|
| 342 |
+
</div>
|
| 343 |
+
<span className="text-sm font-bold text-primary tabular-nums">72%</span>
|
| 344 |
+
</div>
|
| 345 |
+
<div className="h-1.5 w-full bg-surface-3 rounded-full overflow-hidden">
|
| 346 |
+
<div className="h-full w-[72%] bg-gradient-to-r from-primary to-secondary rounded-full relative overflow-hidden">
|
| 347 |
+
<div className="absolute inset-y-0 right-0 w-8 bg-gradient-to-r from-transparent to-white/30 animate-shimmer" />
|
| 348 |
+
</div>
|
| 349 |
+
</div>
|
| 350 |
+
<div className="flex items-center gap-3 text-xs text-muted">
|
| 351 |
+
<span className="flex items-center gap-1"><Icon name="clock" size={12} /> ~12s remaining</span>
|
| 352 |
+
<span className="flex items-center gap-1"><Icon name="cpu" size={12} /> GPU: A100</span>
|
| 353 |
+
</div>
|
| 354 |
+
</div>
|
| 355 |
+
</div>
|
| 356 |
+
<div className="absolute top-6 -left-4 glass rounded-xl px-3 py-2 flex items-center gap-2 shadow-lg shadow-black/20 animate-float">
|
| 357 |
+
<div className="w-6 h-6 rounded-md bg-primary/20 flex items-center justify-center"><Icon name="box" size={12} className="text-primary" /></div>
|
| 358 |
+
<div><div className="text-xs font-semibold text-fg">45.8K+ Models</div></div>
|
| 359 |
+
</div>
|
| 360 |
+
<div className="absolute bottom-12 -left-4 glass rounded-xl px-3 py-2 flex items-center gap-2 shadow-lg shadow-black/20 animate-float-delay">
|
| 361 |
+
<div className="w-6 h-6 rounded-md bg-secondary/20 flex items-center justify-center"><Icon name="zap" size={12} className="text-secondary" /></div>
|
| 362 |
+
<div><div className="text-xs font-semibold text-fg">99.9% Uptime</div></div>
|
| 363 |
+
</div>
|
| 364 |
+
<div className="absolute top-1/2 -right-4 glass rounded-xl px-3 py-2 flex items-center gap-2 shadow-lg shadow-black/20 animate-float-slow hidden lg:flex">
|
| 365 |
+
<div className="w-6 h-6 rounded-md bg-success/20 flex items-center justify-center"><Icon name="dollar-sign" size={12} className="text-success" /></div>
|
| 366 |
+
<div><div className="text-xs font-semibold text-fg">$12M+ Earned</div></div>
|
| 367 |
+
</div>
|
| 368 |
+
</div>
|
| 369 |
+
</div>
|
| 370 |
+
</div>
|
| 371 |
+
</section>
|
| 372 |
+
);
|
| 373 |
+
};
|
| 374 |
+
|
| 375 |
+
const TrustedBy = () => {
|
| 376 |
+
const brands = [
|
| 377 |
+
{ name: 'Stability AI', icon: 'hexagon' }, { name: 'Hugging Face', icon: 'smile' }, { name: 'Replicate', icon: 'copy' },
|
| 378 |
+
{ name: 'Comfy UI', icon: 'layout-dashboard' }, { name: 'Automatic1111', icon: 'terminal' }, { name: 'Ollama', icon: 'server' },
|
| 379 |
+
{ name: 'LangChain', icon: 'link' }, { name: 'LlamaIndex', icon: 'book-open' }, { name: 'CrewAI', icon: 'users' },
|
| 380 |
+
{ name: 'AutoGen', icon: 'bot' }, { name: 'Vercel', icon: 'triangle' }, { name: 'Cloudflare', icon: 'cloud' },
|
| 381 |
+
];
|
| 382 |
+
return (
|
| 383 |
+
<section className="py-12 border-y border-border/50 relative overflow-hidden">
|
| 384 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mb-6">
|
| 385 |
+
<p className="text-center text-xs font-semibold uppercase tracking-widest text-muted">Integrated with the tools you love</p>
|
| 386 |
+
</div>
|
| 387 |
+
<div className="relative">
|
| 388 |
+
<div className="absolute left-0 top-0 bottom-0 w-32 bg-gradient-to-r from-bg to-transparent z-10 pointer-events-none" />
|
| 389 |
+
<div className="absolute right-0 top-0 bottom-0 w-32 bg-gradient-to-l from-bg to-transparent z-10 pointer-events-none" />
|
| 390 |
+
<div className="flex animate-marquee whitespace-nowrap">
|
| 391 |
+
{[...brands, ...brands].map((b, i) => (
|
| 392 |
+
<div key={i} className="mx-8 flex items-center gap-2.5 text-muted/50 hover:text-muted transition-colors duration-300">
|
| 393 |
+
<Icon name={b.icon} size={18} /><span className="text-sm font-semibold tracking-wide">{b.name}</span>
|
| 394 |
+
</div>
|
| 395 |
+
))}
|
| 396 |
+
</div>
|
| 397 |
+
</div>
|
| 398 |
+
</section>
|
| 399 |
+
);
|
| 400 |
+
};
|
| 401 |
+
|
| 402 |
+
const HowItWorks = () => {
|
| 403 |
+
const steps = [
|
| 404 |
+
{ icon: 'pencil', title: '1. Describe', desc: 'Write your prompt or upload a reference image to guide the AI generation.' },
|
| 405 |
+
{ icon: 'sparkles', title: '2. Generate', desc: 'Choose from 45K+ models and watch your creation come to life in seconds.' },
|
| 406 |
+
{ icon: 'wand', title: '3. Refine', desc: "Upscale, edit, and fine-tune with our built-in tools until it's perfect." },
|
| 407 |
+
{ icon: 'rocket', title: '4. Publish & Earn', desc: 'Share to the marketplace or deploy via API and start earning revenue.' },
|
| 408 |
+
];
|
| 409 |
+
return (
|
| 410 |
+
<section className="py-24 relative">
|
| 411 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 412 |
+
<div className="text-center mb-16 reveal">
|
| 413 |
+
<span className="inline-block px-3 py-1.5 rounded-full border border-border bg-surface/50 text-xs font-semibold uppercase tracking-widest text-muted mb-4">How It Works</span>
|
| 414 |
+
<h2 className="text-3xl md:text-5xl font-bold mb-4">From Idea to <span className="gradient-text">Creation</span> in Seconds</h2>
|
| 415 |
+
<p className="text-muted max-w-2xl mx-auto text-lg">Four simple steps to bring your vision to life.</p>
|
| 416 |
+
</div>
|
| 417 |
+
<div className="grid grid-cols-1 md:grid-cols-4 gap-8 reveal reveal-delay-2">
|
| 418 |
+
{steps.map((step, i) => (
|
| 419 |
+
<div key={i} className="relative text-center group">
|
| 420 |
+
<div className="w-16 h-16 rounded-2xl bg-primary/10 flex items-center justify-center mx-auto mb-5 group-hover:scale-110 transition-transform">
|
| 421 |
+
<Icon name={step.icon} size={28} className="text-primary" />
|
| 422 |
+
</div>
|
| 423 |
+
<h3 className="text-lg font-semibold text-fg mb-2">{step.title}</h3>
|
| 424 |
+
<p className="text-sm text-muted leading-relaxed">{step.desc}</p>
|
| 425 |
+
{i < steps.length - 1 && (
|
| 426 |
+
<div className="hidden md:block absolute top-8 right-0 translate-x-1/2 w-8 border-t-2 border-dashed border-border/50" />
|
| 427 |
+
)}
|
| 428 |
+
</div>
|
| 429 |
+
))}
|
| 430 |
+
</div>
|
| 431 |
+
</div>
|
| 432 |
+
</section>
|
| 433 |
+
);
|
| 434 |
+
};
|
| 435 |
+
|
| 436 |
+
const Features = () => {
|
| 437 |
+
const features = [
|
| 438 |
+
{ icon: 'hexagon', title: 'Studio', desc: 'Generate images, videos, audio, text, and code with state-of-the-art AI models. Multi-modal creation in one place.', color: 'text-primary', bg: 'bg-primary/10', link: 'Open Studio →', href: 'generator.html', span: 'lg:col-span-2 lg:row-span-2' },
|
| 439 |
+
{ icon: 'box', title: 'Models', desc: 'Access 45K+ AI models across every modality. Train, fine-tune, and deploy.', color: 'text-secondary', bg: 'bg-secondary/10', link: 'Browse Models →', href: 'models.html', span: '' },
|
| 440 |
+
{ icon: 'bot', title: 'Agents', desc: 'Create autonomous AI agents that think, plan, and execute for you.', color: 'text-success', bg: 'bg-success/10', link: 'Manage Agents →', href: 'dashboard.html', span: '' },
|
| 441 |
+
{ icon: 'workflow', title: 'Workflows', desc: 'Build powerful automation pipelines with visual tools and smart triggers.', color: 'text-purple-400', bg: 'bg-purple-500/10', link: 'Create Workflow →', href: 'workflows.html', span: '' },
|
| 442 |
+
{ icon: 'shopping-bag', title: 'Marketplace', desc: 'Discover, buy, and sell models, agents, prompts, and workflows.', color: 'text-accent', bg: 'bg-accent/10', link: 'Explore Marketplace →', href: 'marketplace.html', span: '' },
|
| 443 |
+
];
|
| 444 |
+
return (
|
| 445 |
+
<section className="py-24 relative">
|
| 446 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 447 |
+
<div className="text-center mb-16 reveal">
|
| 448 |
+
<span className="inline-block px-3 py-1.5 rounded-full border border-border bg-surface/50 text-xs font-semibold uppercase tracking-widest text-muted mb-4">Platform</span>
|
| 449 |
+
<h2 className="text-3xl md:text-5xl font-bold mb-4">Everything You Need to <span className="gradient-text">Build, Scale & Earn</span></h2>
|
| 450 |
+
<p className="text-muted max-w-2xl mx-auto text-lg">One platform for all your AI creation needs. From generation to monetization.</p>
|
| 451 |
+
</div>
|
| 452 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
| 453 |
+
{features.map((f, i) => (
|
| 454 |
+
<div key={i} className={`${f.span || ''} group relative rounded-2xl p-6 lg:p-8 bg-surface border border-border hover:border-surface-3 transition-all duration-300 cursor-pointer card-highlight card-tilt reveal reveal-delay-${i + 1}`}>
|
| 455 |
+
{i === 0 && (
|
| 456 |
+
<div className="hidden lg:block absolute bottom-6 right-6 w-48 h-32 rounded-xl overflow-hidden opacity-50 group-hover:opacity-70 transition-opacity">
|
| 457 |
+
<img src="http://static.photos/cyberpunk/400x200/3" alt="" className="w-full h-full object-cover" />
|
| 458 |
+
</div>
|
| 459 |
+
)}
|
| 460 |
+
<div className={`w-10 h-10 rounded-xl ${f.bg} flex items-center justify-center mb-4 group-hover:scale-110 transition-transform`}>
|
| 461 |
+
<Icon name={f.icon} size={20} className={f.color} />
|
| 462 |
+
</div>
|
| 463 |
+
<h3 className="text-xl font-semibold mb-2 text-fg">{f.title}</h3>
|
| 464 |
+
<p className="text-sm text-muted leading-relaxed mb-6">{f.desc}</p>
|
| 465 |
+
<a href={f.href} className={`text-sm font-semibold ${f.color} flex items-center gap-1 group-hover:gap-2 transition-all`}>{f.link}</a>
|
| 466 |
+
</div>
|
| 467 |
+
))}
|
| 468 |
+
</div>
|
| 469 |
+
</div>
|
| 470 |
+
</section>
|
| 471 |
+
);
|
| 472 |
+
};
|
| 473 |
+
|
| 474 |
+
const NumberStats = () => {
|
| 475 |
+
const [c1, r1] = useCountUp(2800000);
|
| 476 |
+
const [c2, r2] = useCountUp(45800);
|
| 477 |
+
const [c3, r3] = useCountUp(1200000);
|
| 478 |
+
const [c4, r4] = useCountUp(99);
|
| 479 |
+
|
| 480 |
+
const format = (n) => {
|
| 481 |
+
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
|
| 482 |
+
if (n >= 1000) return (n / 1000).toFixed(1) + 'K';
|
| 483 |
+
return n.toString();
|
| 484 |
+
};
|
| 485 |
+
|
| 486 |
+
const stats = [
|
| 487 |
+
{ label: 'Creators', count: c1, ref: r1, display: format(c1), suffix: '+', icon: 'users', color: 'text-primary' },
|
| 488 |
+
{ label: 'AI Models', count: c2, ref: r2, display: format(c2), suffix: '+', icon: 'box', color: 'text-secondary' },
|
| 489 |
+
{ label: 'Workflows Run', count: c3, ref: r3, display: format(c3), suffix: '+', icon: 'workflow', color: 'text-success' },
|
| 490 |
+
{ label: 'Uptime', count: c4, ref: r4, display: c4, suffix: '.9%', icon: 'activity', color: 'text-accent' },
|
| 491 |
+
];
|
| 492 |
+
|
| 493 |
+
return (
|
| 494 |
+
<section className="py-16 border-y border-border/50 bg-surface/30">
|
| 495 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 496 |
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8">
|
| 497 |
+
{stats.map((s, i) => (
|
| 498 |
+
<div key={i} ref={s.ref} className="text-center">
|
| 499 |
+
<div className="w-10 h-10 rounded-xl bg-surface-2 flex items-center justify-center mx-auto mb-3">
|
| 500 |
+
<Icon name={s.icon} size={20} className={s.color} />
|
| 501 |
+
</div>
|
| 502 |
+
<div className="text-3xl md:text-4xl font-bold stat-number tabular-nums">
|
| 503 |
+
{s.display}{s.suffix}
|
| 504 |
+
</div>
|
| 505 |
+
<div className="text-sm text-muted mt-1">{s.label}</div>
|
| 506 |
+
</div>
|
| 507 |
+
))}
|
| 508 |
+
</div>
|
| 509 |
+
</div>
|
| 510 |
+
</section>
|
| 511 |
+
);
|
| 512 |
+
};
|
| 513 |
+
|
| 514 |
+
const LiveActivity = () => {
|
| 515 |
+
const activities = [
|
| 516 |
+
{ user: '@neon_dreamer', action: 'generated Cyberpunk Girl', time: '2s', avatar: 'http://static.photos/people/100x100/1', type: 'image' },
|
| 517 |
+
{ user: '@ai_architect', action: 'created a new workflow', time: '5s', avatar: 'http://static.photos/people/100x100/2', type: 'workflow' },
|
| 518 |
+
{ user: '@prompt_master', action: 'trained DreamVision XL', time: '8s', avatar: 'http://static.photos/people/100x100/3', type: 'model' },
|
| 519 |
+
{ user: '@digital_ghost', action: 'sold a model for $49', time: '12s', avatar: 'http://static.photos/people/100x100/4', type: 'sale' },
|
| 520 |
+
{ user: '@luna_vfx', action: 'rendered 4K video', time: '18s', avatar: 'http://static.photos/people/100x100/15', type: 'video' },
|
| 521 |
+
];
|
| 522 |
+
const creations = [
|
| 523 |
+
{ title: 'Neon Metropolis', type: 'Text to Image', image: 'http://static.photos/cyberpunk/400x300/3', likes: 128, views: '1.2K', user: '@neon_dreamer' },
|
| 524 |
+
{ title: 'Anime Cinematic', type: 'Image to Video', image: 'http://static.photos/people/400x300/5', likes: 95, views: '843', user: '@creative_chaos' },
|
| 525 |
+
{ title: 'Future Soldier', type: 'Text to Video', image: 'http://static.photos/technology/400x300/6', likes: 73, views: '612', user: '@ai_architect' },
|
| 526 |
+
{ title: 'Space Horizon', type: 'Text to Image', image: 'http://static.photos/nature/400x300/7', likes: 66, views: '512', user: '@galactic_prompt' },
|
| 527 |
+
];
|
| 528 |
+
const typeIcons = { image: 'image', workflow: 'workflow', model: 'box', sale: 'dollar-sign', video: 'video' };
|
| 529 |
+
return (
|
| 530 |
+
<section className="py-24 bg-surface/40 border-y border-border/50">
|
| 531 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 532 |
+
<div className="flex flex-col lg:flex-row gap-10">
|
| 533 |
+
<div className="lg:w-[340px] flex-shrink-0 reveal">
|
| 534 |
+
<div className="flex items-center justify-between mb-6">
|
| 535 |
+
<div><h3 className="text-2xl font-bold mb-1">Live Activity</h3><p className="text-sm text-muted">Real-time creator activity</p></div>
|
| 536 |
+
<span className="flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-success/10 border border-success/20 text-xs font-semibold text-success">
|
| 537 |
+
<span className="relative flex h-2 w-2"><span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-success opacity-75"></span><span className="relative inline-flex rounded-full h-2 w-2 bg-success"></span></span>Live
|
| 538 |
+
</span>
|
| 539 |
+
</div>
|
| 540 |
+
<div className="space-y-2 mb-6">
|
| 541 |
+
{activities.map((act, i) => (
|
| 542 |
+
<div key={i} className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-2/50 transition-colors cursor-pointer group">
|
| 543 |
+
<img src={act.avatar} alt="" className="w-9 h-9 rounded-full border border-border object-cover" />
|
| 544 |
+
<div className="flex-1 min-w-0"><p className="text-sm text-fg/80 truncate"><span className="text-fg font-medium">{act.user}</span> {act.action}</p></div>
|
| 545 |
+
<div className="flex items-center gap-1.5 text-xs text-muted shrink-0"><Icon name={typeIcons[act.type]} size={12} />{act.time}</div>
|
| 546 |
+
</div>
|
| 547 |
+
))}
|
| 548 |
+
</div>
|
| 549 |
+
<a href="dashboard.html" className="w-full py-2.5 rounded-xl border border-border text-sm font-medium text-muted hover:text-fg hover:bg-surface-2 transition-colors text-center block">View Full Feed</a>
|
| 550 |
+
</div>
|
| 551 |
+
<div className="flex-1 grid grid-cols-1 sm:grid-cols-2 gap-4 reveal reveal-delay-2">
|
| 552 |
+
{creations.map((item, i) => (
|
| 553 |
+
<div key={i} className="group relative rounded-2xl overflow-hidden aspect-[4/3] cursor-pointer">
|
| 554 |
+
<img src={item.image} alt={item.title} className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110" />
|
| 555 |
+
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/30 to-transparent opacity-80 group-hover:opacity-100 transition-opacity" />
|
| 556 |
+
<div className="absolute top-3 left-3"><span className="px-2.5 py-1 rounded-lg bg-black/60 backdrop-blur-sm text-xs font-medium text-fg border border-white/10">{item.type}</span></div>
|
| 557 |
+
<div className="absolute bottom-0 left-0 right-0 p-4">
|
| 558 |
+
<h4 className="font-semibold text-fg mb-1.5">{item.title}</h4>
|
| 559 |
+
<div className="flex items-center justify-between text-xs text-muted">
|
| 560 |
+
<span>{item.user}</span>
|
| 561 |
+
<div className="flex items-center gap-3">
|
| 562 |
+
<span className="flex items-center gap-1"><Icon name="heart" size={12} /> {item.likes}</span>
|
| 563 |
+
<span className="flex items-center gap-1"><Icon name="eye" size={12} /> {item.views}</span>
|
| 564 |
+
</div>
|
| 565 |
+
</div>
|
| 566 |
+
</div>
|
| 567 |
+
</div>
|
| 568 |
+
))}
|
| 569 |
+
</div>
|
| 570 |
+
</div>
|
| 571 |
+
</div>
|
| 572 |
+
</section>
|
| 573 |
+
);
|
| 574 |
+
};
|
| 575 |
+
|
| 576 |
+
const TopCreators = () => {
|
| 577 |
+
const creators = [
|
| 578 |
+
{ name: 'neon_dreamer', handle: '@neon_dreamer', followers: '24.1K', likes: '1.2M', badge: 'Top Creator', badgeColor: 'bg-primary/15 text-primary border-primary/20', img: 'http://static.photos/people/200x200/10', verified: false },
|
| 579 |
+
{ name: 'ai_architect', handle: '@ai_architect', followers: '18.7K', likes: '982K', badge: 'Verified', badgeColor: 'bg-secondary/15 text-secondary border-secondary/20', img: 'http://static.photos/people/200x200/11', verified: true },
|
| 580 |
+
{ name: 'creative_chaos', handle: '@creative_chaos', followers: '16.3K', likes: '872K', badge: 'Top Creator', badgeColor: 'bg-primary/15 text-primary border-primary/20', img: 'http://static.photos/people/200x200/12', verified: false },
|
| 581 |
+
{ name: 'digital_ghost', handle: '@digital_ghost', followers: '13.9K', likes: '745K', badge: 'Verified', badgeColor: 'bg-secondary/15 text-secondary border-secondary/20', img: 'http://static.photos/people/200x200/13', verified: true },
|
| 582 |
+
{ name: 'prompt_master', handle: '@prompt_master', followers: '12.2K', likes: '612K', badge: 'Rising Star', badgeColor: 'bg-accent/15 text-accent border-accent/20', img: 'http://static.photos/people/200x200/14', verified: false },
|
| 583 |
+
];
|
| 584 |
+
return (
|
| 585 |
+
<section className="py-24">
|
| 586 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 587 |
+
<div className="flex items-center justify-between mb-12 reveal">
|
| 588 |
+
<div><h2 className="text-3xl md:text-4xl font-bold mb-2">Top Creators</h2><p className="text-muted">Meet the community shaping the future.</p></div>
|
| 589 |
+
<a href="marketplace.html" className="hidden sm:inline-flex items-center gap-2 px-4 py-2.5 rounded-xl border border-border text-sm font-medium text-muted hover:text-fg hover:bg-surface-2 transition-colors">Explore All <Icon name="arrow-right" size={14} /></a>
|
| 590 |
+
</div>
|
| 591 |
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4 reveal reveal-delay-2">
|
| 592 |
+
{creators.map((c, i) => (
|
| 593 |
+
<div key={i} className="group relative rounded-2xl p-6 bg-surface border border-border hover:border-surface-3 text-center transition-all duration-300 hover:-translate-y-1">
|
| 594 |
+
<div className="relative w-20 h-20 mx-auto mb-4">
|
| 595 |
+
<img src={c.img} alt={c.name} className="w-full h-full rounded-full object-cover border-2 border-border" />
|
| 596 |
+
{c.verified && <div className="absolute -bottom-0.5 -right-0.5 bg-secondary text-white p-1 rounded-full border-2 border-surface"><Icon name="check" size={10} /></div>}
|
| 597 |
+
</div>
|
| 598 |
+
<h3 className="font-bold text-fg mb-0.5">{c.name}</h3>
|
| 599 |
+
<p className="text-xs text-muted mb-4">{c.handle}</p>
|
| 600 |
+
<div className="flex justify-center gap-4 mb-4 text-xs">
|
| 601 |
+
<div><div className="font-bold text-fg tabular-nums">{c.followers}</div><div className="text-muted">Followers</div></div>
|
| 602 |
+
<div><div className="font-bold text-fg tabular-nums">{c.likes}</div><div className="text-muted">Likes</div></div>
|
| 603 |
+
</div>
|
| 604 |
+
<span className={`inline-block px-3 py-1 rounded-full text-xs font-semibold border ${c.badgeColor}`}>{c.badge}</span>
|
| 605 |
+
</div>
|
| 606 |
+
))}
|
| 607 |
+
</div>
|
| 608 |
+
</div>
|
| 609 |
+
</section>
|
| 610 |
+
);
|
| 611 |
+
};
|
| 612 |
+
|
| 613 |
+
const Pricing = () => {
|
| 614 |
+
const [isYearly, setIsYearly] = useState(false);
|
| 615 |
+
const plans = [
|
| 616 |
+
{ name: 'Free', desc: 'Get started with basic features.', price: 0, period: '/mo', features: ['10 generations / day', 'Basic models access', 'Community support', 'Standard queue'], cta: 'Get Started', highlight: false },
|
| 617 |
+
{ name: 'Creator', desc: 'Perfect for content creators.', price: 19, period: '/mo', features: ['1,000 generations / mo', 'Advanced models', 'Priority support', 'Commercial use', 'Custom workflows'], cta: 'Choose Plan', highlight: false },
|
| 618 |
+
{ name: 'Pro', desc: 'Best for power users.', price: 49, period: '/mo', features: ['10,000 generations / mo', 'All models access', 'Fast queue', 'Early access features', 'API access', 'Team collaboration'], cta: 'Choose Plan', highlight: true },
|
| 619 |
+
{ name: 'Team', desc: 'For teams and studios.', price: 149, period: '/mo', features: ['50,000 generations / mo', 'Team collaboration', 'Shared assets', 'Advanced analytics', 'API access', 'Priority queue'], cta: 'Choose Plan', highlight: false },
|
| 620 |
+
{ name: 'Enterprise', desc: 'For large scale operations.', price: null, period: '', features: ['Unlimited generations', 'Custom integrations', 'Dedicated support', 'SLA & uptime guarantee', 'On-premise option'], cta: 'Contact Sales', highlight: false },
|
| 621 |
+
];
|
| 622 |
+
return (
|
| 623 |
+
<section id="pricing" className="py-24 relative">
|
| 624 |
+
<div className="absolute inset-0 dot-pattern opacity-20 pointer-events-none" />
|
| 625 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
| 626 |
+
<div className="text-center mb-12 reveal">
|
| 627 |
+
<span className="inline-block px-3 py-1.5 rounded-full border border-border bg-surface/50 text-xs font-semibold uppercase tracking-widest text-muted mb-4">Pricing</span>
|
| 628 |
+
<h2 className="text-3xl md:text-5xl font-bold mb-4">Simple, Transparent Pricing</h2>
|
| 629 |
+
<p className="text-muted text-lg mb-8">Start free. Scale when you're ready.</p>
|
| 630 |
+
<div className="inline-flex items-center p-1 rounded-full border border-border bg-surface">
|
| 631 |
+
<button onClick={() => setIsYearly(false)} className={`px-6 py-2.5 rounded-full text-sm font-medium transition-all ${!isYearly ? 'bg-fg text-bg shadow-sm' : 'text-muted hover:text-fg'}`}>Monthly</button>
|
| 632 |
+
<button onClick={() => setIsYearly(true)} className={`px-6 py-2.5 rounded-full text-sm font-medium transition-all flex items-center gap-2 ${isYearly ? 'bg-fg text-bg shadow-sm' : 'text-muted hover:text-fg'}`}>
|
| 633 |
+
Yearly <span className="px-2 py-0.5 rounded-full bg-success/15 text-success text-xs font-semibold border border-success/20">20% off</span>
|
| 634 |
+
</button>
|
| 635 |
+
</div>
|
| 636 |
+
</div>
|
| 637 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 reveal reveal-delay-2">
|
| 638 |
+
{plans.map((plan, i) => (
|
| 639 |
+
<div key={i} className={`relative rounded-2xl p-6 flex flex-col h-full transition-all duration-300 ${plan.highlight ? 'bg-gradient-to-b from-primary/10 to-surface border-2 border-primary/40 shadow-xl shadow-primary/10' : 'bg-surface border border-border hover:border-surface-3'}`}>
|
| 640 |
+
{plan.highlight && <div className="absolute -top-3.5 left-1/2 -translate-x-1/2 px-4 py-1 bg-primary text-primary-foreground text-xs font-bold rounded-full shadow-lg shadow-primary/30">MOST POPULAR</div>}
|
| 641 |
+
<div className="mb-5"><h3 className="text-lg font-semibold text-fg mb-1">{plan.name}</h3><p className="text-xs text-muted">{plan.desc}</p></div>
|
| 642 |
+
<div className="mb-5"><span className="text-4xl font-bold text-fg tabular-nums">{plan.price === null ? 'Custom' : `$${isYearly ? Math.round(plan.price * 0.8) : plan.price}`}</span>{plan.period && <span className="text-muted text-sm">{plan.period}</span>}</div>
|
| 643 |
+
<ul className="space-y-3 mb-8 flex-grow">
|
| 644 |
+
{plan.features.map((feat, j) => <li key={j} className="flex items-start gap-2.5 text-sm text-fg/80"><Icon name="check" size={16} className="text-primary shrink-0 mt-0.5" /><span>{feat}</span></li>)}
|
| 645 |
+
</ul>
|
| 646 |
+
<button className={`w-full py-3 rounded-xl text-sm font-semibold transition-all ${plan.highlight ? 'bg-primary text-primary-foreground hover:brightness-110 shadow-lg shadow-primary/25' : 'border border-border text-fg hover:bg-surface-2 hover:border-surface-3'}`}>{plan.cta}</button>
|
| 647 |
+
</div>
|
| 648 |
+
))}
|
| 649 |
+
</div>
|
| 650 |
+
</div>
|
| 651 |
+
</section>
|
| 652 |
+
);
|
| 653 |
+
};
|
| 654 |
+
|
| 655 |
+
const Testimonials = () => {
|
| 656 |
+
const testimonials = [
|
| 657 |
+
{ text: "NSFWStudio changed the way I create. The tools are insane and the community is next level.", author: "Luna Mist", role: "AI Artist", img: "http://static.photos/people/100x100/20" },
|
| 658 |
+
{ text: "The workflow builder and agents saved me hundreds of hours. Absolute game changer for my studio.", author: "Dark Pixel", role: "Workflow Engineer", img: "http://static.photos/people/100x100/21" },
|
| 659 |
+
{ text: "Best platform for AI creation. The model quality is unmatched and the API is a breeze.", author: "Echo Nova", role: "Content Creator", img: "http://static.photos/people/100x100/22" },
|
| 660 |
+
{ text: "Finally, a platform that gives creators freedom and powerful tools in one place. Outstanding.", author: "Nyx Reality", role: "Digital Creator", img: "http://static.photos/people/100x100/23" },
|
| 661 |
+
];
|
| 662 |
+
return (
|
| 663 |
+
<section className="py-24 bg-surface/40 border-y border-border/50">
|
| 664 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 665 |
+
<div className="text-center mb-12 reveal">
|
| 666 |
+
<span className="inline-block px-3 py-1.5 rounded-full border border-border bg-surface/50 text-xs font-semibold uppercase tracking-widest text-muted mb-4">Testimonials</span>
|
| 667 |
+
<h2 className="text-3xl md:text-4xl font-bold mb-2">Loved by Creators</h2>
|
| 668 |
+
<p className="text-muted">See what our community is saying.</p>
|
| 669 |
+
</div>
|
| 670 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 reveal reveal-delay-2">
|
| 671 |
+
{testimonials.map((t, i) => (
|
| 672 |
+
<div key={i} className="group rounded-2xl p-6 bg-surface border border-border hover:border-surface-3 transition-all duration-300 hover:-translate-y-1 relative">
|
| 673 |
+
<div className="absolute top-4 right-4 text-primary/10 group-hover:text-primary/20 transition-colors"><Icon name="quote" size={32} /></div>
|
| 674 |
+
<div className="flex gap-0.5 mb-4 text-primary">{[...Array(5)].map((_, j) => <Icon key={j} name="star" size={14} />)}</div>
|
| 675 |
+
<p className="text-fg/80 text-sm leading-relaxed mb-6">"{t.text}"</p>
|
| 676 |
+
<div className="flex items-center gap-3 mt-auto">
|
| 677 |
+
<img src={t.img} alt={t.author} className="w-10 h-10 rounded-full border border-border object-cover" />
|
| 678 |
+
<div><div className="text-sm font-semibold text-fg">{t.author}</div><div className="text-xs text-muted">{t.role}</div></div>
|
| 679 |
+
</div>
|
| 680 |
+
</div>
|
| 681 |
+
))}
|
| 682 |
+
</div>
|
| 683 |
+
</div>
|
| 684 |
+
</section>
|
| 685 |
+
);
|
| 686 |
+
};
|
| 687 |
+
|
| 688 |
+
const FAQ = () => {
|
| 689 |
+
const [openIndex, setOpenIndex] = useState(null);
|
| 690 |
+
const faqs = [
|
| 691 |
+
{ q: 'What is NSFWStudio?', a: "NSFWStudio is an all-in-one AI creation platform that lets you generate images, videos, agents, and workflows. Whether you're a solo creator or part of a team, our tools help you build, automate, and monetize your AI-powered content." },
|
| 692 |
+
{ q: 'Is there a free tier?', a: 'Yes! Our Free tier gives you 10 generations per day, access to basic models, and community support. No credit card required to start. Upgrade anytime as your needs grow.' },
|
| 693 |
+
{ q: 'What kind of models are available?', a: 'We offer over 45,000+ AI models across all modalities — text to image, image to video, style transfer, upscaling, and more. You can also train and deploy your own custom models on the platform.' },
|
| 694 |
+
{ q: 'Can I use generated content commercially?', a: 'Absolutely. On the Creator plan and above, all content you generate comes with full commercial usage rights. You own what you create and can use it for any purpose.' },
|
| 695 |
+
{ q: 'What are AI Agents and Workflows?', a: 'Agents are autonomous AI programs that can think, plan, and execute tasks on your behalf. Workflows let you chain multiple steps together — from generation to post-processing — into automated pipelines.' },
|
| 696 |
+
{ q: 'How does the marketplace work?', a: 'The marketplace lets you buy and sell models, agents, prompts, and workflow templates. Creators earn revenue from their work, and buyers get access to production-ready AI tools. We handle payments, licensing, and distribution.' },
|
| 697 |
+
];
|
| 698 |
+
return (
|
| 699 |
+
<section className="py-24">
|
| 700 |
+
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 701 |
+
<div className="text-center mb-12 reveal">
|
| 702 |
+
<span className="inline-block px-3 py-1.5 rounded-full border border-border bg-surface/50 text-xs font-semibold uppercase tracking-widest text-muted mb-4">FAQ</span>
|
| 703 |
+
<h2 className="text-3xl md:text-4xl font-bold mb-2">Frequently Asked Questions</h2>
|
| 704 |
+
<p className="text-muted">Got questions? We've got answers.</p>
|
| 705 |
+
</div>
|
| 706 |
+
<div className="space-y-3 reveal reveal-delay-1">
|
| 707 |
+
{faqs.map((faq, i) => (
|
| 708 |
+
<div key={i} className="rounded-xl border border-border bg-surface overflow-hidden">
|
| 709 |
+
<button onClick={() => setOpenIndex(openIndex === i ? null : i)} className="w-full flex items-center justify-between p-5 text-left hover:bg-surface-2/50 transition-colors">
|
| 710 |
+
<span className="font-semibold text-fg pr-4">{faq.q}</span>
|
| 711 |
+
<Icon name={openIndex === i ? 'minus' : 'plus'} size={20} className="text-muted shrink-0" />
|
| 712 |
+
</button>
|
| 713 |
+
<div className={`faq-content ${openIndex === i ? 'open' : ''}`}>
|
| 714 |
+
<div><p className="px-5 pb-5 text-sm text-muted leading-relaxed">{faq.a}</p></div>
|
| 715 |
+
</div>
|
| 716 |
+
</div>
|
| 717 |
+
))}
|
| 718 |
+
</div>
|
| 719 |
+
</div>
|
| 720 |
+
</section>
|
| 721 |
+
);
|
| 722 |
+
};
|
| 723 |
+
|
| 724 |
+
const CTA = () => (
|
| 725 |
+
<section className="py-24 relative overflow-hidden">
|
| 726 |
+
<div className="absolute inset-0 grid-pattern opacity-20 pointer-events-none" />
|
| 727 |
+
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] bg-primary/10 rounded-full blur-[200px] pointer-events-none" />
|
| 728 |
+
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10 text-center reveal">
|
| 729 |
+
<h2 className="text-3xl md:text-5xl font-bold mb-6">Ready to Create <span className="gradient-text">Without Limits</span>?</h2>
|
| 730 |
+
<p className="text-muted text-lg max-w-2xl mx-auto mb-10">Join 2.8 million creators who are already building the future with AI. Start free, no credit card required.</p>
|
| 731 |
+
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-8">
|
| 732 |
+
<input type="email" placeholder="Enter your email" className="w-full sm:w-auto flex-1 max-w-md px-5 py-4 rounded-xl bg-surface border border-border text-fg placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all text-base" />
|
| 733 |
+
<a href="signin.html" className="group inline-flex items-center justify-center gap-2.5 px-8 py-4 rounded-xl bg-primary text-primary-foreground font-semibold hover:brightness-110 transition-all shadow-xl shadow-primary/25 hover:shadow-primary/40 whitespace-nowrap">
|
| 734 |
+
Start Creating <Icon name="arrow-right" size={18} className="group-hover:translate-x-1 transition-transform" />
|
| 735 |
+
</a>
|
| 736 |
+
</div>
|
| 737 |
+
<div className="flex flex-wrap items-center justify-center gap-6 text-sm text-muted">
|
| 738 |
+
{['No credit card required', 'Free tier available', 'Cancel anytime'].map((text, i) => (
|
| 739 |
+
<span key={i} className="flex items-center gap-1.5"><Icon name="check-circle" size={16} className="text-success" />{text}</span>
|
| 740 |
+
))}
|
| 741 |
+
</div>
|
| 742 |
+
</div>
|
| 743 |
+
</section>
|
| 744 |
+
);
|
| 745 |
+
|
| 746 |
+
const Footer = () => {
|
| 747 |
+
const columns = [
|
| 748 |
+
{ title: 'Platform', links: ['Studio', 'Models', 'Agents', 'Workflows', 'Marketplace', 'Pricing'], hrefs: ['generator.html', 'models.html', 'dashboard.html', 'workflows.html', 'marketplace.html', '#pricing'] },
|
| 749 |
+
{ title: 'Resources', links: ['Documentation', 'API Reference', 'Cookbook', 'Guides', 'Blog', 'Changelog'], hrefs: ['#', '#', '#', '#', '#', '#'] },
|
| 750 |
+
{ title: 'Company', links: ['About Us', 'Careers', 'Contact', 'Partners', 'Brand Kit', 'Press'], hrefs: ['#', '#', '#', '#', '#', '#'] },
|
| 751 |
+
{ title: 'Legal', links: ['Terms of Service', 'Privacy Policy', 'Acceptable Use', 'Content Policy', 'DMCA', 'Cookie Settings'], hrefs: ['#', '#', '#', '#', '#', '#'] },
|
| 752 |
+
];
|
| 753 |
+
return (
|
| 754 |
+
<footer className="pt-20 pb-10 border-t border-border bg-[oklch(0.04_0.005_285)]">
|
| 755 |
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 756 |
+
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-8 mb-16">
|
| 757 |
+
<div className="col-span-2">
|
| 758 |
+
<a href="#" className="flex items-center gap-2.5 mb-4">
|
| 759 |
+
<div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div>
|
| 760 |
+
<span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span>
|
| 761 |
+
</a>
|
| 762 |
+
<p className="text-sm text-muted mb-6 max-w-xs leading-relaxed">The ultimate AI platform for creators. Build, automate, and monetize without limits.</p>
|
| 763 |
+
<div className="mb-6">
|
| 764 |
+
<p className="text-sm font-medium text-fg mb-3">Stay in the loop</p>
|
| 765 |
+
<div className="flex gap-2">
|
| 766 |
+
<input type="email" placeholder="you@example.com" className="flex-1 px-4 py-2.5 rounded-lg bg-surface-2 border border-border text-sm text-fg placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all min-w-0" />
|
| 767 |
+
<button className="px-4 py-2.5 rounded-lg bg-primary text-primary-foreground text-sm font-semibold hover:brightness-110 transition-all shrink-0">Subscribe</button>
|
| 768 |
+
</div>
|
| 769 |
+
</div>
|
| 770 |
+
<div className="flex gap-3">
|
| 771 |
+
{[{ name: 'github' }, { name: 'twitter' }, { name: 'youtube' }, { name: 'instagram' }].map((s) => (
|
| 772 |
+
<a key={s.name} href="#" className="w-9 h-9 rounded-lg bg-surface-2 flex items-center justify-center text-muted hover:text-fg hover:bg-surface-3 transition-colors border border-border"><Icon name={s.name} size={16} /></a>
|
| 773 |
+
))}
|
| 774 |
+
</div>
|
| 775 |
+
</div>
|
| 776 |
+
{columns.map((col, i) => (
|
| 777 |
+
<div key={i}>
|
| 778 |
+
<h4 className="font-semibold text-fg mb-4 text-sm">{col.title}</h4>
|
| 779 |
+
<ul className="space-y-2.5">{col.links.map((link, j) => <li key={j}><a href={col.hrefs[j]} className="text-sm text-muted hover:text-fg transition-colors">{link}</a></li>)}</ul>
|
| 780 |
+
</div>
|
| 781 |
+
))}
|
| 782 |
+
</div>
|
| 783 |
+
<div className="flex flex-col md:flex-row items-center justify-between pt-8 border-t border-border/50 gap-4">
|
| 784 |
+
<div className="flex flex-col sm:flex-row items-center gap-4 sm:gap-6 text-xs text-muted">
|
| 785 |
+
<span>© 2025 NSFWStudio. All rights reserved.</span>
|
| 786 |
+
<span className="hidden sm:inline text-border">·</span>
|
| 787 |
+
<span>18 U.S.C. § 2257 Compliance</span>
|
| 788 |
+
</div>
|
| 789 |
+
<div className="flex items-center gap-2 text-xs text-muted"><span className="w-2 h-2 rounded-full bg-success" /> All Systems Operational</div>
|
| 790 |
+
</div>
|
| 791 |
+
</div>
|
| 792 |
+
</footer>
|
| 793 |
+
);
|
| 794 |
+
};
|
| 795 |
+
|
| 796 |
+
const App = () => {
|
| 797 |
+
useScrollReveal();
|
| 798 |
+
return (
|
| 799 |
+
<div className="min-h-screen bg-bg text-fg overflow-x-hidden">
|
| 800 |
+
<TopBar />
|
| 801 |
+
<ScrollProgress />
|
| 802 |
+
<Navbar />
|
| 803 |
+
<main>
|
| 804 |
+
<Hero />
|
| 805 |
+
<TrustedBy />
|
| 806 |
+
<HowItWorks />
|
| 807 |
+
<Features />
|
| 808 |
+
<NumberStats />
|
| 809 |
+
<LiveActivity />
|
| 810 |
+
<TopCreators />
|
| 811 |
+
<Pricing />
|
| 812 |
+
<Testimonials />
|
| 813 |
+
<FAQ />
|
| 814 |
+
<CTA />
|
| 815 |
+
</main>
|
| 816 |
+
<Footer />
|
| 817 |
+
<BackToTop />
|
| 818 |
+
</div>
|
| 819 |
+
);
|
| 820 |
+
};
|
| 821 |
+
|
| 822 |
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
| 823 |
+
</script>
|
| 824 |
+
<script src="https://deepsite.hf.co/deepsite-badge.js"></script>
|
| 825 |
+
</body>
|
| 826 |
+
</html>
|
marketplace.html
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Marketplace — NSFWStudio</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
| 9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| 10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 15 |
+
<script>
|
| 16 |
+
tailwind.config = { theme: { extend: { fontFamily: { sans: ['Inter','system-ui','sans-serif'], mono: ['JetBrains Mono','monospace'] }, colors: { bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)', fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)', secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)', destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)' } } } }
|
| 17 |
+
</script>
|
| 18 |
+
<style>
|
| 19 |
+
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
| 20 |
+
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
|
| 21 |
+
.gradient-text { background: linear-gradient(135deg, oklch(0.70 0.28 350), oklch(0.75 0.15 215)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
| 22 |
+
::selection { background: oklch(0.70 0.28 350 / 0.3); color: white; }
|
| 23 |
+
body::after { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat:repeat; background-size:256px 256px; }
|
| 24 |
+
.no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
| 25 |
+
.tabular-nums { font-variant-numeric: tabular-nums; }
|
| 26 |
+
</style>
|
| 27 |
+
</head>
|
| 28 |
+
<body class="antialiased">
|
| 29 |
+
<div id="root"></div>
|
| 30 |
+
<script type="text/babel">
|
| 31 |
+
const { useState } = React;
|
| 32 |
+
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
|
| 33 |
+
const ref = React.useRef(null);
|
| 34 |
+
React.useEffect(() => { if (ref.current) { const existing = ref.current.querySelector('svg'); if (existing) existing.remove(); const svg = lucide.createElement(lucide.icons[name]); if (svg) { svg.setAttribute('width',size); svg.setAttribute('height',size); svg.setAttribute('stroke-width',strokeWidth); ref.current.appendChild(svg); } } }, [name, size, className, strokeWidth]);
|
| 35 |
+
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
const navItems = [
|
| 39 |
+
{ name: 'Dashboard', icon: 'layout-dashboard', href: 'dashboard.html' },
|
| 40 |
+
{ name: 'Generator', icon: 'sparkles', href: 'generator.html' },
|
| 41 |
+
{ name: 'Playground', icon: 'code', href: 'playground.html' },
|
| 42 |
+
{ name: 'Models', icon: 'box', href: 'models.html' },
|
| 43 |
+
{ name: 'Workflows', icon: 'workflow', href: 'workflows.html' },
|
| 44 |
+
{ name: 'Marketplace', icon: 'shopping-bag', href: 'marketplace.html', active: true },
|
| 45 |
+
{ name: 'API Keys', icon: 'key', href: 'api-keys.html' },
|
| 46 |
+
{ divider: true },
|
| 47 |
+
{ name: 'Profile', icon: 'user', href: 'profile.html' },
|
| 48 |
+
{ name: 'Settings', icon: 'settings', href: 'settings.html' },
|
| 49 |
+
];
|
| 50 |
+
|
| 51 |
+
const Sidebar = ({ isOpen, setIsOpen }) => (
|
| 52 |
+
<>
|
| 53 |
+
<div className={`fixed inset-0 z-40 lg:hidden transition-all ${isOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
|
| 54 |
+
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setIsOpen(false)} />
|
| 55 |
+
</div>
|
| 56 |
+
<aside className={`fixed top-0 left-0 bottom-0 z-50 w-64 bg-surface border-r border-border flex flex-col transition-transform duration-300 lg:translate-x-0 ${isOpen ? 'translate-x-0' : '-translate-x-full'}`}>
|
| 57 |
+
<div className="p-4 flex items-center gap-2.5 border-b border-border">
|
| 58 |
+
<div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div>
|
| 59 |
+
<span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span>
|
| 60 |
+
</div>
|
| 61 |
+
<nav className="flex-1 p-3 space-y-1 overflow-y-auto no-scrollbar">
|
| 62 |
+
{navItems.map((item, i) => item.divider ? <div key={i} className="my-3 border-t border-border" /> : (
|
| 63 |
+
<a key={i} href={item.href} className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors ${item.active ? 'bg-primary/10 text-primary border border-primary/20' : 'text-muted hover:text-fg hover:bg-surface-2 border border-transparent'}`}><Icon name={item.icon} size={18} />{item.name}</a>
|
| 64 |
+
))}
|
| 65 |
+
</nav>
|
| 66 |
+
<div className="p-4 border-t border-border">
|
| 67 |
+
<a href="documentation.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="book-open" size={18} />Documentation</a>
|
| 68 |
+
<a href="api-reference.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="file-code" size={18} />API Reference</a>
|
| 69 |
+
</div>
|
| 70 |
+
</aside>
|
| 71 |
+
</>
|
| 72 |
+
);
|
| 73 |
+
|
| 74 |
+
const App = () => {
|
| 75 |
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
| 76 |
+
const [activeTab, setActiveTab] = useState('All');
|
| 77 |
+
const tabs = ['All', 'Models', 'Agents', 'Prompts', 'Workflows', 'Templates'];
|
| 78 |
+
|
| 79 |
+
const items = [
|
| 80 |
+
{ name: 'DreamVision XL', author: '@neon_dreamer', price: '$9.99', rating: 4.9, reviews: '2.4K', category: 'Models', downloads: '45.2K', img: 'http://static.photos/cyberpunk/400x300/10', hot: true },
|
| 81 |
+
{ name: 'CyberPunk v3', author: '@ai_architect', price: '$4.99', rating: 4.8, reviews: '1.8K', category: 'Models', downloads: '28.1K', img: 'http://static.photos/technology/400x300/11', hot: false },
|
| 82 |
+
{ name: 'Auto Tagger Agent', author: '@prompt_master', price: 'Free', rating: 4.7, reviews: '892', category: 'Agents', downloads: '12.3K', img: 'http://static.photos/abstract/400x300/12', hot: false },
|
| 83 |
+
{ name: 'Anime Diffusion', author: '@creative_chaos', price: '$7.99', rating: 4.9, reviews: '3.1K', category: 'Models', downloads: '52K', img: 'http://static.photos/people/400x300/13', hot: true },
|
| 84 |
+
{ name: 'Smart Prompt Pack', author: '@luna_vfx', price: '$2.99', rating: 4.6, reviews: '456', category: 'Prompts', downloads: '8.2K', img: 'http://static.photos/minimal/400x300/14', hot: false },
|
| 85 |
+
{ name: 'Batch Processor', author: '@digital_ghost', price: 'Free', rating: 4.5, reviews: '234', category: 'Workflows', downloads: '5.1K', img: 'http://static.photos/workspace/400x300/15', hot: false },
|
| 86 |
+
{ name: 'Fantasy Art XL', author: '@galactic_prompt', price: '$5.99', rating: 4.8, reviews: '1.2K', category: 'Models', downloads: '19.4K', img: 'http://static.photos/nature/400x300/16', hot: false },
|
| 87 |
+
{ name: 'Content Filter Agent', author: '@dark_pixel', price: 'Free', rating: 4.4, reviews: '178', category: 'Agents', downloads: '3.7K', img: 'http://static.photos/red/400x300/17', hot: false },
|
| 88 |
+
{ name: 'Photo Real v2', author: '@echo_nova', price: '$14.99', rating: 4.9, reviews: '4.2K', category: 'Models', downloads: '67K', img: 'http://static.photos/travel/400x300/18', hot: true },
|
| 89 |
+
];
|
| 90 |
+
|
| 91 |
+
const filtered = activeTab === 'All' ? items : items.filter(i => i.category === activeTab);
|
| 92 |
+
|
| 93 |
+
return (
|
| 94 |
+
<div className="min-h-screen bg-bg">
|
| 95 |
+
<Sidebar isOpen={sidebarOpen} setIsOpen={setSidebarOpen} />
|
| 96 |
+
<div className="lg:ml-64">
|
| 97 |
+
<header className="sticky top-0 z-30 glass border-b border-border">
|
| 98 |
+
<div className="flex items-center justify-between px-4 sm:px-6 h-16">
|
| 99 |
+
<div className="flex items-center gap-4">
|
| 100 |
+
<button onClick={() => setSidebarOpen(true)} className="lg:hidden p-2 rounded-lg hover:bg-surface-2 text-muted"><Icon name="menu" size={20} /></button>
|
| 101 |
+
<h1 className="text-xl font-bold">Marketplace</h1>
|
| 102 |
+
</div>
|
| 103 |
+
<div className="flex items-center gap-3">
|
| 104 |
+
<div className="hidden sm:flex items-center gap-2 px-4 py-2 rounded-xl bg-surface-2 border border-border text-muted text-sm w-64">
|
| 105 |
+
<Icon name="search" size={16} /><span>Search marketplace...</span>
|
| 106 |
+
</div>
|
| 107 |
+
<button className="p-2 rounded-lg hover:bg-surface-2 text-muted relative"><Icon name="bell" size={20} /><span className="absolute top-1.5 right-1.5 w-2 h-2 rounded-full bg-primary" /></button>
|
| 108 |
+
<a href="profile.html" className="w-9 h-9 rounded-lg overflow-hidden border border-border"><img src="http://static.photos/people/100x100/10" alt="" className="w-full h-full object-cover" /></a>
|
| 109 |
+
</div>
|
| 110 |
+
</div>
|
| 111 |
+
</header>
|
| 112 |
+
|
| 113 |
+
<main className="p-4 sm:p-6 lg:p-8">
|
| 114 |
+
{/* Hero Banner */}
|
| 115 |
+
<div className="relative rounded-2xl overflow-hidden mb-8 p-8 lg:p-12 bg-surface border border-border">
|
| 116 |
+
<div className="absolute inset-0 opacity-20"><img src="http://static.photos/cyberpunk/1200x400/1" alt="" className="w-full h-full object-cover" /><div className="absolute inset-0 bg-gradient-to-r from-bg via-bg/80 to-transparent" /></div>
|
| 117 |
+
<div className="relative z-10 max-w-lg">
|
| 118 |
+
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-primary/20 border border-primary/30 text-primary text-xs font-semibold mb-4"><Icon name="zap" size={12} /> Featured</span>
|
| 119 |
+
<h2 className="text-2xl lg:text-3xl font-bold mb-3">Discover & Sell AI <span className="gradient-text">Creations</span></h2>
|
| 120 |
+
<p className="text-muted mb-6">Browse thousands of models, agents, prompts, and workflows from top creators.</p>
|
| 121 |
+
<div className="flex gap-3">
|
| 122 |
+
<a href="#" className="px-6 py-3 rounded-xl bg-primary text-primary-foreground font-semibold hover:brightness-110 transition-all">Sell Your Work</a>
|
| 123 |
+
<a href="#" className="px-6 py-3 rounded-xl border border-border text-fg font-semibold hover:bg-surface-2 transition-colors">Learn More</a>
|
| 124 |
+
</div>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
|
| 128 |
+
{/* Tabs */}
|
| 129 |
+
<div className="flex items-center gap-2 mb-6 overflow-x-auto no-scrollbar pb-1">
|
| 130 |
+
{tabs.map(tab => (
|
| 131 |
+
<button key={tab} onClick={() => setActiveTab(tab)} className={`px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap transition-colors ${activeTab === tab ? 'bg-primary text-primary-foreground' : 'text-muted hover:text-fg hover:bg-surface-2'}`}>{tab}</button>
|
| 132 |
+
))}
|
| 133 |
+
</div>
|
| 134 |
+
|
| 135 |
+
{/* Grid */}
|
| 136 |
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
| 137 |
+
{filtered.map((item, i) => (
|
| 138 |
+
<div key={i} className="group rounded-2xl bg-surface border border-border hover:border-surface-3 overflow-hidden transition-all hover:-translate-y-0.5">
|
| 139 |
+
<div className="relative aspect-[4/3] overflow-hidden">
|
| 140 |
+
<img src={item.img} alt={item.name} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" />
|
| 141 |
+
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
|
| 142 |
+
{item.hot && <span className="absolute top-3 left-3 px-2.5 py-1 rounded-lg bg-primary text-primary-foreground text-xs font-bold flex items-center gap-1"><Icon name="flame" size={12} /> Hot</span>}
|
| 143 |
+
<span className="absolute top-3 right-3 px-2.5 py-1 rounded-lg bg-black/50 backdrop-blur-sm text-xs font-medium text-fg border border-white/10">{item.category}</span>
|
| 144 |
+
</div>
|
| 145 |
+
<div className="p-4">
|
| 146 |
+
<div className="flex items-start justify-between gap-2 mb-2">
|
| 147 |
+
<div>
|
| 148 |
+
<h3 className="font-semibold text-fg">{item.name}</h3>
|
| 149 |
+
<p className="text-xs text-muted">{item.author}</p>
|
| 150 |
+
</div>
|
| 151 |
+
<span className="text-sm font-bold text-primary whitespace-nowrap">{item.price}</span>
|
| 152 |
+
</div>
|
| 153 |
+
<div className="flex items-center justify-between text-xs text-muted">
|
| 154 |
+
<div className="flex items-center gap-1"><Icon name="star" size={12} className="text-accent" /> <span className="text-fg">{item.rating}</span> ({item.reviews})</div>
|
| 155 |
+
<div className="flex items-center gap-1"><Icon name="download" size={12} /> {item.downloads}</div>
|
| 156 |
+
</div>
|
| 157 |
+
</div>
|
| 158 |
+
</div>
|
| 159 |
+
))}
|
| 160 |
+
</div>
|
| 161 |
+
</main>
|
| 162 |
+
</div>
|
| 163 |
+
</div>
|
| 164 |
+
);
|
| 165 |
+
};
|
| 166 |
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
| 167 |
+
</script>
|
| 168 |
+
</body>
|
| 169 |
+
</html>
|
models.html
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Models — NSFWStudio</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
| 9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| 10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 15 |
+
<script>
|
| 16 |
+
tailwind.config = { theme: { extend: { fontFamily: { sans: ['Inter','system-ui','sans-serif'], mono: ['JetBrains Mono','monospace'] }, colors: { bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)', fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)', secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)', destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)' } } } }
|
| 17 |
+
</script>
|
| 18 |
+
<style>
|
| 19 |
+
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
| 20 |
+
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
|
| 21 |
+
::selection { background: oklch(0.70 0.28 350 / 0.3); }
|
| 22 |
+
body::after { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat:repeat; background-size:256px 256px; }
|
| 23 |
+
.no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
| 24 |
+
.tabular-nums { font-variant-numeric: tabular-nums; }
|
| 25 |
+
</style>
|
| 26 |
+
</head>
|
| 27 |
+
<body class="antialiased">
|
| 28 |
+
<div id="root"></div>
|
| 29 |
+
<script type="text/babel">
|
| 30 |
+
const { useState } = React;
|
| 31 |
+
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
|
| 32 |
+
const ref = React.useRef(null);
|
| 33 |
+
React.useEffect(() => { if (ref.current) { const existing = ref.current.querySelector('svg'); if (existing) existing.remove(); const svg = lucide.createElement(lucide.icons[name]); if (svg) { svg.setAttribute('width',size); svg.setAttribute('height',size); svg.setAttribute('stroke-width',strokeWidth); ref.current.appendChild(svg); } } }, [name, size, className, strokeWidth]);
|
| 34 |
+
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
const navItems = [
|
| 38 |
+
{ name: 'Dashboard', icon: 'layout-dashboard', href: 'dashboard.html' },
|
| 39 |
+
{ name: 'Generator', icon: 'sparkles', href: 'generator.html' },
|
| 40 |
+
{ name: 'Playground', icon: 'code', href: 'playground.html' },
|
| 41 |
+
{ name: 'Models', icon: 'box', href: 'models.html', active: true },
|
| 42 |
+
{ name: 'Workflows', icon: 'workflow', href: 'workflows.html' },
|
| 43 |
+
{ name: 'Marketplace', icon: 'shopping-bag', href: 'marketplace.html' },
|
| 44 |
+
{ name: 'API Keys', icon: 'key', href: 'api-keys.html' },
|
| 45 |
+
{ divider: true },
|
| 46 |
+
{ name: 'Profile', icon: 'user', href: 'profile.html' },
|
| 47 |
+
{ name: 'Settings', icon: 'settings', href: 'settings.html' },
|
| 48 |
+
];
|
| 49 |
+
|
| 50 |
+
const Sidebar = ({ isOpen, setIsOpen }) => (
|
| 51 |
+
<>
|
| 52 |
+
<div className={`fixed inset-0 z-40 lg:hidden transition-all ${isOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
|
| 53 |
+
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setIsOpen(false)} />
|
| 54 |
+
</div>
|
| 55 |
+
<aside className={`fixed top-0 left-0 bottom-0 z-50 w-64 bg-surface border-r border-border flex flex-col transition-transform duration-300 lg:translate-x-0 ${isOpen ? 'translate-x-0' : '-translate-x-full'}`}>
|
| 56 |
+
<div className="p-4 flex items-center gap-2.5 border-b border-border"><div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div><span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span></div>
|
| 57 |
+
<nav className="flex-1 p-3 space-y-1 overflow-y-auto no-scrollbar">{navItems.map((item, i) => item.divider ? <div key={i} className="my-3 border-t border-border" /> : <a key={i} href={item.href} className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors ${item.active ? 'bg-primary/10 text-primary border border-primary/20' : 'text-muted hover:text-fg hover:bg-surface-2 border border-transparent'}`}><Icon name={item.icon} size={18} />{item.name}</a>)}</nav>
|
| 58 |
+
<div className="p-4 border-t border-border"><a href="documentation.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="book-open" size={18} />Documentation</a><a href="api-reference.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="file-code" size={18} />API Reference</a></div>
|
| 59 |
+
</aside>
|
| 60 |
+
</>
|
| 61 |
+
);
|
| 62 |
+
|
| 63 |
+
const App = () => {
|
| 64 |
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
| 65 |
+
const [activeTab, setActiveTab] = useState('my-models');
|
| 66 |
+
const [viewMode, setViewMode] = useState('grid');
|
| 67 |
+
|
| 68 |
+
const myModels = [
|
| 69 |
+
{ name: 'DreamVision XL', type: 'Image Generation', status: 'published', runs: '45.2K', rating: 4.9, revenue: '$2,340', updated: '2 days ago', img: 'http://static.photos/cyberpunk/400x300/20' },
|
| 70 |
+
{ name: 'CyberPunk v3', type: 'Image Generation', status: 'published', runs: '28.1K', rating: 4.8, revenue: '$1,890', updated: '1 week ago', img: 'http://static.photos/technology/400x300/21' },
|
| 71 |
+
{ name: 'MorphGAN v2', type: 'Style Transfer', status: 'draft', runs: '0', rating: '-', revenue: '$0', updated: '3 days ago', img: 'http://static.photos/abstract/400x300/22' },
|
| 72 |
+
{ name: 'Photo Enhancer Pro', type: 'Upscaling', status: 'review', runs: '12.8K', rating: 4.7, revenue: '$890', updated: '5 days ago', img: 'http://static.photos/nature/400x300/23' },
|
| 73 |
+
];
|
| 74 |
+
|
| 75 |
+
const exploreModels = [
|
| 76 |
+
{ name: 'Anime Diffusion', type: 'Image Generation', author: '@anime_king', runs: '52K', rating: 4.9, price: '$7.99', img: 'http://static.photos/people/400x300/24' },
|
| 77 |
+
{ name: 'Photo Real v2', type: 'Photorealistic', author: '@real_photo', runs: '67K', rating: 4.9, price: '$14.99', img: 'http://static.photos/travel/400x300/25' },
|
| 78 |
+
{ name: 'Fantasy Art XL', type: 'Fantasy', author: '@fantasy_art', runs: '19.4K', rating: 4.8, price: '$5.99', img: 'http://static.photos/nature/400x300/26' },
|
| 79 |
+
{ name: 'Ink & Wash', type: 'Art Style', author: '@ink_master', runs: '15.3K', rating: 4.7, price: 'Free', img: 'http://static.photos/minimal/400x300/27' },
|
| 80 |
+
{ name: 'FutureCosmos', type: 'Sci-Fi', author: '@cosmic_ai', runs: '8.7K', rating: 4.6, price: '$3.99', img: 'http://static.photos/aerial/400x300/28' },
|
| 81 |
+
{ name: 'Vintagify', type: 'Style Transfer', author: '@retro_vibes', runs: '22.1K', rating: 4.8, price: 'Free', img: 'http://static.photos/vintage/400x300/29' },
|
| 82 |
+
];
|
| 83 |
+
|
| 84 |
+
const statusColors = { published: 'bg-success/10 text-success border-success/20', draft: 'bg-muted/10 text-muted border-border', review: 'bg-accent/10 text-accent border-accent/20' };
|
| 85 |
+
|
| 86 |
+
return (
|
| 87 |
+
<div className="min-h-screen bg-bg">
|
| 88 |
+
<Sidebar isOpen={sidebarOpen} setIsOpen={setSidebarOpen} />
|
| 89 |
+
<div className="lg:ml-64">
|
| 90 |
+
<header className="sticky top-0 z-30 glass border-b border-border">
|
| 91 |
+
<div className="flex items-center justify-between px-4 sm:px-6 h-16">
|
| 92 |
+
<div className="flex items-center gap-4">
|
| 93 |
+
<button onClick={() => setSidebarOpen(true)} className="lg:hidden p-2 rounded-lg hover:bg-surface-2 text-muted"><Icon name="menu" size={20} /></button>
|
| 94 |
+
<h1 className="text-xl font-bold">Models</h1>
|
| 95 |
+
</div>
|
| 96 |
+
<div className="flex items-center gap-3">
|
| 97 |
+
<div className="hidden sm:flex items-center gap-2 px-3 py-2 rounded-xl bg-surface-2 border border-border text-muted text-sm w-56"><Icon name="search" size={16} /><span>Search models...</span></div>
|
| 98 |
+
<a href="#" className="px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-semibold hover:brightness-110 transition-all flex items-center gap-2"><Icon name="plus" size={16} />Train Model</a>
|
| 99 |
+
<a href="profile.html" className="w-9 h-9 rounded-lg overflow-hidden border border-border"><img src="http://static.photos/people/100x100/10" alt="" className="w-full h-full object-cover" /></a>
|
| 100 |
+
</div>
|
| 101 |
+
</div>
|
| 102 |
+
</header>
|
| 103 |
+
|
| 104 |
+
<main className="p-4 sm:p-6 lg:p-8">
|
| 105 |
+
{/* Tabs */}
|
| 106 |
+
<div className="flex items-center gap-2 mb-6 border-b border-border">
|
| 107 |
+
{[{ id: 'my-models', label: 'My Models' }, { id: 'explore', label: 'Explore' }, { id: 'training', label: 'Training' }].map(t => (
|
| 108 |
+
<button key={t.id} onClick={() => setActiveTab(t.id)} className={`px-4 py-3 border-b-2 text-sm font-medium transition-colors -mb-px ${activeTab === t.id ? 'border-primary text-primary' : 'border-transparent text-muted hover:text-fg'}`}>{t.label}</button>
|
| 109 |
+
))}
|
| 110 |
+
</div>
|
| 111 |
+
|
| 112 |
+
{activeTab === 'my-models' && (
|
| 113 |
+
<div className="space-y-4">
|
| 114 |
+
{myModels.map((m, i) => (
|
| 115 |
+
<div key={i} className="group rounded-2xl bg-surface border border-border hover:border-surface-3 transition-all overflow-hidden">
|
| 116 |
+
<div className="flex flex-col sm:flex-row">
|
| 117 |
+
<div className="sm:w-48 h-32 sm:h-auto shrink-0 overflow-hidden">
|
| 118 |
+
<img src={m.img} alt={m.name} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" />
|
| 119 |
+
</div>
|
| 120 |
+
<div className="flex-1 p-5">
|
| 121 |
+
<div className="flex items-start justify-between gap-3 mb-3">
|
| 122 |
+
<div>
|
| 123 |
+
<div className="flex items-center gap-2 mb-1">
|
| 124 |
+
<h3 className="font-semibold text-fg">{m.name}</h3>
|
| 125 |
+
<span className={`px-2.5 py-0.5 rounded-full text-xs font-semibold border ${statusColors[m.status]}`}>{m.status}</span>
|
| 126 |
+
</div>
|
| 127 |
+
<p className="text-sm text-muted">{m.type}</p>
|
| 128 |
+
</div>
|
| 129 |
+
<div className="text-right">
|
| 130 |
+
<div className="text-lg font-bold text-fg tabular-nums">{m.revenue}</div>
|
| 131 |
+
<div className="text-xs text-muted">Revenue</div>
|
| 132 |
+
</div>
|
| 133 |
+
</div>
|
| 134 |
+
<div className="flex items-center gap-6 text-sm text-muted">
|
| 135 |
+
<span className="flex items-center gap-1"><Icon name="play" size={14} />{m.runs} runs</span>
|
| 136 |
+
<span className="flex items-center gap-1"><Icon name="star" size={14} className="text-accent" />{m.rating}</span>
|
| 137 |
+
<span className="flex items-center gap-1"><Icon name="clock" size={14} />{m.updated}</span>
|
| 138 |
+
</div>
|
| 139 |
+
<div className="flex items-center gap-2 mt-4">
|
| 140 |
+
<button className="px-3 py-1.5 rounded-lg bg-primary text-primary-foreground text-sm font-medium hover:brightness-110 transition-all">Manage</button>
|
| 141 |
+
<button className="px-3 py-1.5 rounded-lg border border-border text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors">Analytics</button>
|
| 142 |
+
<button className="px-3 py-1.5 rounded-lg border border-border text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors">Edit</button>
|
| 143 |
+
</div>
|
| 144 |
+
</div>
|
| 145 |
+
</div>
|
| 146 |
+
</div>
|
| 147 |
+
))}
|
| 148 |
+
</div>
|
| 149 |
+
)}
|
| 150 |
+
|
| 151 |
+
{activeTab === 'explore' && (
|
| 152 |
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
| 153 |
+
{exploreModels.map((m, i) => (
|
| 154 |
+
<div key={i} className="group rounded-2xl bg-surface border border-border hover:border-surface-3 overflow-hidden transition-all hover:-translate-y-0.5">
|
| 155 |
+
<div className="aspect-[4/3] overflow-hidden relative">
|
| 156 |
+
<img src={m.img} alt={m.name} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" />
|
| 157 |
+
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
|
| 158 |
+
<span className="absolute top-3 right-3 px-2.5 py-1 rounded-lg bg-black/50 backdrop-blur-sm text-xs font-medium border border-white/10">{m.type}</span>
|
| 159 |
+
</div>
|
| 160 |
+
<div className="p-4">
|
| 161 |
+
<div className="flex items-start justify-between mb-2">
|
| 162 |
+
<div>
|
| 163 |
+
<h3 className="font-semibold text-fg">{m.name}</h3>
|
| 164 |
+
<p className="text-xs text-muted">{m.author}</p>
|
| 165 |
+
</div>
|
| 166 |
+
<span className="text-sm font-bold text-primary">{m.price}</span>
|
| 167 |
+
</div>
|
| 168 |
+
<div className="flex items-center gap-4 text-xs text-muted">
|
| 169 |
+
<span className="flex items-center gap-1"><Icon name="star" size={12} className="text-accent" />{m.rating}</span>
|
| 170 |
+
<span className="flex items-center gap-1"><Icon name="download" size={12} />{m.runs}</span>
|
| 171 |
+
</div>
|
| 172 |
+
</div>
|
| 173 |
+
</div>
|
| 174 |
+
))}
|
| 175 |
+
</div>
|
| 176 |
+
)}
|
| 177 |
+
|
| 178 |
+
{activeTab === 'training' && (
|
| 179 |
+
<div className="text-center py-16">
|
| 180 |
+
<div className="w-20 h-20 rounded-2xl bg-surface-2 flex items-center justify-center mx-auto mb-4"><Icon name="cpu" size={32} className="text-muted" /></div>
|
| 181 |
+
<h2 className="text-xl font-semibold mb-2">No Active Training</h2>
|
| 182 |
+
<p className="text-muted mb-6 max-w-md mx-auto">Start training a new model by uploading your dataset and configuring your training parameters.</p>
|
| 183 |
+
<a href="#" className="inline-flex items-center gap-2 px-6 py-3 rounded-xl bg-primary text-primary-foreground font-semibold hover:brightness-110 transition-all"><Icon name="plus" size={18} />Start Training</a>
|
playground.html
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Playground — NSFWStudio</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
| 9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| 10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 15 |
+
<script>
|
| 16 |
+
tailwind.config = { theme: { extend: { fontFamily: { sans: ['Inter','system-ui','sans-serif'], mono: ['JetBrains Mono','monospace'] }, colors: { bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)', fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)', secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)', destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)' } } } }
|
| 17 |
+
</script>
|
| 18 |
+
<style>
|
| 19 |
+
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
| 20 |
+
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
|
| 21 |
+
::selection { background: oklch(0.70 0.28 350 / 0.3); }
|
| 22 |
+
body::after { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat:repeat; background-size:256px 256px; }
|
| 23 |
+
.no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
| 24 |
+
</style>
|
| 25 |
+
</head>
|
| 26 |
+
<body class="antialiased">
|
| 27 |
+
<div id="root"></div>
|
| 28 |
+
<script type="text/babel">
|
| 29 |
+
const { useState } = React;
|
| 30 |
+
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
|
| 31 |
+
const ref = React.useRef(null);
|
| 32 |
+
React.useEffect(() => { if (ref.current) { const existing = ref.current.querySelector('svg'); if (existing) existing.remove(); const svg = lucide.createElement(lucide.icons[name]); if (svg) { svg.setAttribute('width',size); svg.setAttribute('height',size); svg.setAttribute('stroke-width',strokeWidth); ref.current.appendChild(svg); } } }, [name, size, className, strokeWidth]);
|
| 33 |
+
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
|
| 34 |
+
};
|
| 35 |
+
|
| 36 |
+
const navItems = [
|
| 37 |
+
{ name: 'Dashboard', icon: 'layout-dashboard', href: 'dashboard.html' },
|
| 38 |
+
{ name: 'Generator', icon: 'sparkles', href: 'generator.html' },
|
| 39 |
+
{ name: 'Playground', icon: 'code', href: 'playground.html', active: true },
|
| 40 |
+
{ name: 'Models', icon: 'box', href: 'models.html' },
|
| 41 |
+
{ name: 'Workflows', icon: 'workflow', href: 'workflows.html' },
|
| 42 |
+
{ name: 'Marketplace', icon: 'shopping-bag', href: 'marketplace.html' },
|
| 43 |
+
{ name: 'API Keys', icon: 'key', href: 'api-keys.html' },
|
| 44 |
+
{ divider: true },
|
| 45 |
+
{ name: 'Profile', icon: 'user', href: 'profile.html' },
|
| 46 |
+
{ name: 'Settings', icon: 'settings', href: 'settings.html' },
|
| 47 |
+
];
|
| 48 |
+
|
| 49 |
+
const Sidebar = ({ isOpen, setIsOpen }) => (
|
| 50 |
+
<>
|
| 51 |
+
<div className={`fixed inset-0 z-40 lg:hidden transition-all ${isOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
|
| 52 |
+
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setIsOpen(false)} />
|
| 53 |
+
</div>
|
| 54 |
+
<aside className={`fixed top-0 left-0 bottom-0 z-50 w-64 bg-surface border-r border-border flex flex-col transition-transform duration-300 lg:translate-x-0 ${isOpen ? 'translate-x-0' : '-translate-x-full'}`}>
|
| 55 |
+
<div className="p-4 flex items-center gap-2.5 border-b border-border"><div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div><span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span></div>
|
| 56 |
+
<nav className="flex-1 p-3 space-y-1 overflow-y-auto no-scrollbar">{navItems.map((item, i) => item.divider ? <div key={i} className="my-3 border-t border-border" /> : <a key={i} href={item.href} className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors ${item.active ? 'bg-primary/10 text-primary border border-primary/20' : 'text-muted hover:text-fg hover:bg-surface-2 border border-transparent'}`}><Icon name={item.icon} size={18} />{item.name}</a>)}</nav>
|
| 57 |
+
<div className="p-4 border-t border-border"><a href="documentation.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="book-open" size={18} />Documentation</a><a href="api-reference.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="file-code" size={18} />API Reference</a></div>
|
| 58 |
+
</aside>
|
| 59 |
+
</>
|
| 60 |
+
);
|
| 61 |
+
|
| 62 |
+
const App = () => {
|
| 63 |
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
| 64 |
+
const [selectedModel, setSelectedModel] = useState('DreamVision XL');
|
| 65 |
+
const [prompt, setPrompt] = useState('');
|
| 66 |
+
const [isRunning, setIsRunning] = useState(false);
|
| 67 |
+
|
| 68 |
+
const models = ['DreamVision XL', 'SDXL 1.0', 'CyberPunk v3', 'Anime Diffusion', 'Photo Real v2', 'Fantasy Art XL'];
|
| 69 |
+
const history = [
|
| 70 |
+
{ prompt: 'A neon cityscape at night with rain', model: 'DreamVision XL', time: '2 min ago', status: 'success' },
|
| 71 |
+
{ prompt: 'Cyberpunk anime girl with glowing eyes', model: 'Anime Diffusion', time: '8 min ago', status: 'success' },
|
| 72 |
+
{ prompt: 'Dark forest with mystical creatures', model: 'Fantasy Art XL', time: '15 min ago', status: 'error' },
|
| 73 |
+
{ prompt: 'Futuristic soldier in power armor', model: 'SDXL 1.0', time: '22 min ago', status: 'success' },
|
| 74 |
+
];
|
| 75 |
+
|
| 76 |
+
const handleRun = () => { setIsRunning(true); setTimeout(() => setIsRunning(false), 3000); };
|
| 77 |
+
|
| 78 |
+
return (
|
| 79 |
+
<div className="min-h-screen bg-bg">
|
| 80 |
+
<Sidebar isOpen={sidebarOpen} setIsOpen={setSidebarOpen} />
|
| 81 |
+
<div className="lg:ml-64">
|
| 82 |
+
<header className="sticky top-0 z-30 glass border-b border-border">
|
| 83 |
+
<div className="flex items-center justify-between px-4 sm:px-6 h-16">
|
| 84 |
+
<div className="flex items-center gap-4">
|
| 85 |
+
<button onClick={() => setSidebarOpen(true)} className="lg:hidden p-2 rounded-lg hover:bg-surface-2 text-muted"><Icon name="menu" size={20} /></button>
|
| 86 |
+
<h1 className="text-xl font-bold">API Playground</h1>
|
| 87 |
+
</div>
|
| 88 |
+
<div className="flex items-center gap-3">
|
| 89 |
+
<select className="px-3 py-2 rounded-lg bg-surface-2 border border-border text-sm text-fg" value={selectedModel} onChange={e => setSelectedModel(e.target.value)}>
|
| 90 |
+
{models.map(m => <option key={m} value={m}>{m}</option>)}
|
| 91 |
+
</select>
|
| 92 |
+
<a href="profile.html" className="w-9 h-9 rounded-lg overflow-hidden border border-border"><img src="http://static.photos/people/100x100/10" alt="" className="w-full h-full object-cover" /></a>
|
| 93 |
+
</div>
|
| 94 |
+
</div>
|
| 95 |
+
</header>
|
| 96 |
+
|
| 97 |
+
<div className="flex h-[calc(100vh-64px)]">
|
| 98 |
+
{/* Left Panel - Config */}
|
| 99 |
+
<div className="w-80 border-r border-border flex flex-col overflow-y-auto no-scrollbar hidden md:flex">
|
| 100 |
+
<div className="p-4 border-b border-border">
|
| 101 |
+
<h3 className="font-semibold text-sm mb-3">Parameters</h3>
|
| 102 |
+
<div className="space-y-4">
|
| 103 |
+
<div>
|
| 104 |
+
<label className="text-xs text-muted mb-1.5 block">Model</label>
|
| 105 |
+
<select className="w-full px-3 py-2.5 rounded-xl bg-surface-2 border border-border text-sm text-fg" value={selectedModel} onChange={e => setSelectedModel(e.target.value)}>
|
| 106 |
+
{models.map(m => <option key={m} value={m}>{m}</option>)}
|
| 107 |
+
</select>
|
| 108 |
+
</div>
|
| 109 |
+
{[
|
| 110 |
+
{ label: 'Width', value: '1024', id: 'w' },
|
| 111 |
+
{ label: 'Height', value: '1024', id: 'h' },
|
| 112 |
+
{ label: 'Steps', value: '30', id: 's' },
|
| 113 |
+
].map(p => (
|
| 114 |
+
<div key={p.id}>
|
| 115 |
+
<label className="text-xs text-muted mb-1.5 block">{p.label}</label>
|
| 116 |
+
<input type="text" defaultValue={p.value} className="w-full px-3 py-2.5 rounded-xl bg-surface-2 border border-border text-sm text-fg focus:outline-none focus:ring-2 focus:ring-primary/50" />
|
| 117 |
+
</div>
|
| 118 |
+
))}
|
| 119 |
+
<div>
|
| 120 |
+
<label className="text-xs text-muted mb-1.5 block">Guidance Scale</label>
|
| 121 |
+
<input type="range" min="1" max="20" defaultValue="7.5" className="w-full accent-primary" />
|
| 122 |
+
<div className="flex justify-between text-xs text-muted mt-1"><span>1</span><span>7.5</span><span>20</span></div>
|
| 123 |
+
</div>
|
| 124 |
+
<div>
|
| 125 |
+
<label className="text-xs text-muted mb-1.5 block">Seed</label>
|
| 126 |
+
<div className="flex gap-2">
|
| 127 |
+
<input type="text" defaultValue="Random" className="flex-1 px-3 py-2.5 rounded-xl bg-surface-2 border border-border text-sm text-fg focus:outline-none focus:ring-2 focus:ring-primary/50" />
|
| 128 |
+
<button className="px-3 py-2.5 rounded-xl bg-surface-2 border border-border text-muted hover:text-fg transition-colors"><Icon name="shuffle" size={16} /></button>
|
| 129 |
+
</div>
|
| 130 |
+
</div>
|
| 131 |
+
<div>
|
| 132 |
+
<label className="text-xs text-muted mb-1.5 block">Negative Prompt</label>
|
| 133 |
+
<textarea className="w-full px-3 py-2.5 rounded-xl bg-surface-2 border border-border text-sm text-fg focus:outline-none focus:ring-2 focus:ring-primary/50 h-20 resize-none" placeholder="blurry, low quality, watermark..." />
|
| 134 |
+
</div>
|
| 135 |
+
</div>
|
| 136 |
+
</div>
|
| 137 |
+
<div className="p-4">
|
| 138 |
+
<h3 className="font-semibold text-sm mb-3">History</h3>
|
| 139 |
+
<div className="space-y-2">
|
| 140 |
+
{history.map((h, i) => (
|
| 141 |
+
<div key={i} className="p-3 rounded-xl hover:bg-surface-2/50 cursor-pointer transition-colors">
|
| 142 |
+
<div className="flex items-center justify-between mb-1">
|
| 143 |
+
<span className={`w-2 h-2 rounded-full ${h.status === 'success' ? 'bg-success' : 'bg-destructive'}`} />
|
| 144 |
+
<span className="text-xs text-muted">{h.time}</span>
|
| 145 |
+
</div>
|
| 146 |
+
<p className="text-xs text-fg/80 truncate">{h.prompt}</p>
|
| 147 |
+
<p className="text-xs text-muted mt-0.5">{h.model}</p>
|
| 148 |
+
</div>
|
| 149 |
+
))}
|
| 150 |
+
</div>
|
| 151 |
+
</div>
|
| 152 |
+
</div>
|
| 153 |
+
|
| 154 |
+
{/* Main Content */}
|
| 155 |
+
<div className="flex-1 flex flex-col">
|
| 156 |
+
{/* Code & Result tabs */}
|
| 157 |
+
<div className="flex-1 flex flex-col overflow-hidden">
|
| 158 |
+
<div className="flex items-center gap-1 px-4 pt-4 pb-2">
|
| 159 |
+
<button className="px-4 py-2 rounded-lg text-sm font-medium bg-primary text-primary-foreground">Request</button>
|
| 160 |
+
<button className="px-4 py-2 rounded-lg text-sm font-medium text-muted hover:text-fg hover:bg-surface-2 transition-colors">Response</button>
|
| 161 |
+
<button className="px-4 py-2 rounded-lg text-sm font-medium text-muted hover:text-fg hover:bg-surface-2 transition-colors">cURL</button>
|
| 162 |
+
<button className="px-4 py-2 rounded-lg text-sm font-medium text-muted hover:text-fg hover:bg-surface-2 transition-colors">Python</button>
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
+
<div className="flex-1 px-4">
|
| 166 |
+
<textarea className="w-full h-full min-h-[200px] px-4 py-3 rounded-xl bg-surface border border-border text-sm text-fg font-mono focus:outline-none focus:ring-2 focus:ring-primary/50 resize-none" value={`{\n "model": "${selectedModel}",\n "prompt": "A stunning neon cityscape...",\n "width": 1024,\n "height": 1024,\n "steps": 30,\n "guidance_scale": 7.5,\n "seed": null\n}`} readOnly />
|
| 167 |
+
</div>
|
| 168 |
+
</div>
|
| 169 |
+
|
| 170 |
+
{/* Prompt bar */}
|
| 171 |
+
<div className="p-4 border-t border-border">
|
| 172 |
+
<div className="flex gap-3">
|
| 173 |
+
<div className="flex-1 flex items-center gap-2 px-4 py-3 rounded-xl bg-surface border border-border focus-within:ring-2 focus-within:ring-primary/50">
|
| 174 |
+
<Icon name="terminal" size={18} className="text-muted" />
|
| 175 |
+
<input type="text" value={prompt} onChange={e => setPrompt(e.target.value)} placeholder="Enter your prompt..." className="flex-1 bg-transparent text-fg placeholder:text-muted outline-none text-sm" />
|
| 176 |
+
</div>
|
| 177 |
+
<button onClick={handleRun} disabled={isRunning} className={`px-6 py-3 rounded-xl font-semibold transition-all flex items-center gap-2 ${isRunning ? 'bg-surface-2 border border-border text-muted' : 'bg-primary text-primary-foreground hover:brightness-110 shadow-lg shadow-primary/25'}`}>
|
| 178 |
+
{isRunning ? <><span className="w-4 h-4 border-2 border-muted border-t-transparent rounded-full animate-spin" />Running...</> : <><Icon name="play" size={18} />Run</>}
|
| 179 |
+
</button>
|
| 180 |
+
</div>
|
| 181 |
+
</div>
|
| 182 |
+
</div>
|
| 183 |
+
</div>
|
| 184 |
+
</div>
|
| 185 |
+
</div>
|
| 186 |
+
);
|
| 187 |
+
};
|
| 188 |
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
| 189 |
+
</script>
|
| 190 |
+
</body>
|
| 191 |
+
</html>
|
signin.html
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Sign In — NSFWStudio</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
| 9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| 10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 15 |
+
<script>
|
| 16 |
+
tailwind.config = {
|
| 17 |
+
theme: {
|
| 18 |
+
extend: {
|
| 19 |
+
fontFamily: { sans: ['Inter','system-ui','sans-serif'], mono: ['JetBrains Mono','monospace'] },
|
| 20 |
+
colors: {
|
| 21 |
+
bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)',
|
| 22 |
+
fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)',
|
| 23 |
+
secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)',
|
| 24 |
+
destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)',
|
| 25 |
+
},
|
| 26 |
+
},
|
| 27 |
+
},
|
| 28 |
+
}
|
| 29 |
+
</script>
|
| 30 |
+
<style>
|
| 31 |
+
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
| 32 |
+
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
|
| 33 |
+
.gradient-text { background: linear-gradient(135deg, oklch(0.70 0.28 350), oklch(0.75 0.15 215)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
| 34 |
+
::selection { background: oklch(0.70 0.28 350 / 0.3); color: white; }
|
| 35 |
+
body::after { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat:repeat; background-size:256px 256px; }
|
| 36 |
+
</style>
|
| 37 |
+
</head>
|
| 38 |
+
<body class="antialiased">
|
| 39 |
+
<div id="root"></div>
|
| 40 |
+
<script type="text/babel">
|
| 41 |
+
const { useState } = React;
|
| 42 |
+
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
|
| 43 |
+
const ref = React.useRef(null);
|
| 44 |
+
React.useEffect(() => {
|
| 45 |
+
if (ref.current) { const existing = ref.current.querySelector('svg'); if (existing) existing.remove(); const svg = lucide.createElement(lucide.icons[name]); if (svg) { svg.setAttribute('width',size); svg.setAttribute('height',size); svg.setAttribute('stroke-width',strokeWidth); ref.current.appendChild(svg); } }
|
| 46 |
+
}, [name, size, className, strokeWidth]);
|
| 47 |
+
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
const App = () => {
|
| 51 |
+
const [isSignUp, setIsSignUp] = useState(false);
|
| 52 |
+
const [showPassword, setShowPassword] = useState(false);
|
| 53 |
+
const [email, setEmail] = useState('');
|
| 54 |
+
const [password, setPassword] = useState('');
|
| 55 |
+
const [name, setName] = useState('');
|
| 56 |
+
|
| 57 |
+
return (
|
| 58 |
+
<div className="min-h-screen flex">
|
| 59 |
+
{/* Left Panel - Visual */}
|
| 60 |
+
<div className="hidden lg:flex lg:w-1/2 relative overflow-hidden bg-surface items-center justify-center">
|
| 61 |
+
<div className="absolute inset-0">
|
| 62 |
+
<img src="http://static.photos/cyberpunk/1200x630/1" alt="" className="w-full h-full object-cover opacity-40" />
|
| 63 |
+
<div className="absolute inset-0 bg-gradient-to-r from-bg via-bg/60 to-transparent" />
|
| 64 |
+
<div className="absolute inset-0 bg-gradient-to-t from-bg via-transparent to-transparent" />
|
| 65 |
+
</div>
|
| 66 |
+
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-primary/20 rounded-full blur-[160px] pointer-events-none" />
|
| 67 |
+
<div className="relative z-10 max-w-md px-12">
|
| 68 |
+
<a href="index.html" className="flex items-center gap-2.5 mb-8">
|
| 69 |
+
<div className="w-10 h-10 rounded-xl bg-primary flex items-center justify-center"><Icon name="sparkles" size={20} className="text-primary-foreground" /></div>
|
| 70 |
+
<span className="text-2xl font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span>
|
| 71 |
+
</a>
|
| 72 |
+
<h2 className="text-3xl font-bold mb-4 leading-tight">Create <span className="gradient-text">Without Limits</span></h2>
|
| 73 |
+
<p className="text-muted leading-relaxed mb-8">The all-in-one AI platform for creators. Generate images, videos, agents, and workflows — all in one place.</p>
|
| 74 |
+
<div className="flex items-center gap-4">
|
| 75 |
+
{['2.8M+ Creators', '45.8K+ Models', '99.9% Uptime'].map((s, i) => (
|
| 76 |
+
<div key={i} className="glass rounded-xl px-4 py-3">
|
| 77 |
+
<div className="text-sm font-bold text-fg">{s.split(' ')[0]}</div>
|
| 78 |
+
<div className="text-xs text-muted">{s.split(' ').slice(1).join(' ')}</div>
|
| 79 |
+
</div>
|
| 80 |
+
))}
|
| 81 |
+
</div>
|
| 82 |
+
</div>
|
| 83 |
+
</div>
|
| 84 |
+
|
| 85 |
+
{/* Right Panel - Form */}
|
| 86 |
+
<div className="flex-1 flex items-center justify-center p-6 sm:p-12">
|
| 87 |
+
<div className="w-full max-w-md">
|
| 88 |
+
<div className="lg:hidden flex items-center gap-2.5 mb-10">
|
| 89 |
+
<div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div>
|
| 90 |
+
<span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span>
|
| 91 |
+
</div>
|
| 92 |
+
|
| 93 |
+
<h1 className="text-3xl font-bold mb-2">{isSignUp ? 'Create your account' : 'Welcome back'}</h1>
|
| 94 |
+
<p className="text-muted mb-8">{isSignUp ? 'Start creating for free. No credit card required.' : 'Sign in to continue to your dashboard.'}</p>
|
| 95 |
+
|
| 96 |
+
{/* Social Buttons */}
|
| 97 |
+
<div className="grid grid-cols-3 gap-3 mb-6">
|
| 98 |
+
{[
|
| 99 |
+
{ name: 'Google', icon: 'chrome' },
|
| 100 |
+
{ name: 'GitHub', icon: 'github' },
|
| 101 |
+
{ name: 'Discord', icon: 'message-circle' },
|
| 102 |
+
].map(p => (
|
| 103 |
+
<button key={p.name} className="flex items-center justify-center gap-2 px-4 py-3 rounded-xl border border-border bg-surface hover:bg-surface-2 transition-colors text-sm font-medium">
|
| 104 |
+
<Icon name={p.icon} size={18} />
|
| 105 |
+
<span className="hidden sm:inline">{p.name}</span>
|
| 106 |
+
</button>
|
| 107 |
+
))}
|
| 108 |
+
</div>
|
| 109 |
+
|
| 110 |
+
<div className="flex items-center gap-3 mb-6">
|
| 111 |
+
<div className="flex-1 h-px bg-border" />
|
| 112 |
+
<span className="text-xs text-muted uppercase tracking-wider">or</span>
|
| 113 |
+
<div className="flex-1 h-px bg-border" />
|
| 114 |
+
</div>
|
| 115 |
+
|
| 116 |
+
<div className="space-y-4">
|
| 117 |
+
{isSignUp && (
|
| 118 |
+
<div>
|
| 119 |
+
<label className="block text-sm font-medium text-fg mb-2">Full Name</label>
|
| 120 |
+
<div className="relative">
|
| 121 |
+
<div className="absolute left-3.5 top-1/2 -translate-y-1/2 text-muted"><Icon name="user" size={16} /></div>
|
| 122 |
+
<input type="text" value={name} onChange={e => setName(e.target.value)} placeholder="John Doe" className="w-full pl-10 pr-4 py-3 rounded-xl bg-surface border border-border text-fg placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all" />
|
| 123 |
+
</div>
|
| 124 |
+
</div>
|
| 125 |
+
)}
|
| 126 |
+
<div>
|
| 127 |
+
<label className="block text-sm font-medium text-fg mb-2">Email</label>
|
| 128 |
+
<div className="relative">
|
| 129 |
+
<div className="absolute left-3.5 top-1/2 -translate-y-1/2 text-muted"><Icon name="mail" size={16} /></div>
|
| 130 |
+
<input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="you@example.com" className="w-full pl-10 pr-4 py-3 rounded-xl bg-surface border border-border text-fg placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all" />
|
| 131 |
+
</div>
|
| 132 |
+
</div>
|
| 133 |
+
<div>
|
| 134 |
+
<div className="flex items-center justify-between mb-2">
|
| 135 |
+
<label className="block text-sm font-medium text-fg">Password</label>
|
| 136 |
+
{!isSignUp && <a href="#" className="text-xs text-primary hover:underline">Forgot password?</a>}
|
| 137 |
+
</div>
|
| 138 |
+
<div className="relative">
|
| 139 |
+
<div className="absolute left-3.5 top-1/2 -translate-y-1/2 text-muted"><Icon name="lock" size={16} /></div>
|
| 140 |
+
<input type={showPassword ? 'text' : 'password'} value={password} onChange={e => setPassword(e.target.value)} placeholder="••••••••" className="w-full pl-10 pr-12 py-3 rounded-xl bg-surface border border-border text-fg placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all" />
|
| 141 |
+
<button type="button" onClick={() => setShowPassword(!showPassword)} className="absolute right-3.5 top-1/2 -translate-y-1/2 text-muted hover:text-fg transition-colors">
|
| 142 |
+
<Icon name={showPassword ? 'eye-off' : 'eye'} size={16} />
|
| 143 |
+
</button>
|
| 144 |
+
</div>
|
| 145 |
+
</div>
|
| 146 |
+
|
| 147 |
+
{isSignUp && (
|
| 148 |
+
<label className="flex items-start gap-3 cursor-pointer">
|
| 149 |
+
<input type="checkbox" className="mt-1 w-4 h-4 rounded border-border bg-surface text-primary focus:ring-primary/50 accent-primary" />
|
| 150 |
+
<span className="text-xs text-muted leading-relaxed">I agree to the <a href="#" className="text-fg underline">Terms of Service</a> and <a href="#" className="text-fg underline">Privacy Policy</a></span>
|
| 151 |
+
</label>
|
| 152 |
+
)}
|
| 153 |
+
|
| 154 |
+
{!isSignUp && (
|
| 155 |
+
<label className="flex items-center gap-3 cursor-pointer">
|
| 156 |
+
<input type="checkbox" className="w-4 h-4 rounded border-border bg-surface text-primary focus:ring-primary/50 accent-primary" />
|
| 157 |
+
<span className="text-sm text-muted">Remember me</span>
|
| 158 |
+
</label>
|
| 159 |
+
)}
|
| 160 |
+
|
| 161 |
+
<button className="w-full py-3.5 rounded-xl bg-primary text-primary-foreground font-semibold hover:brightness-110 transition-all shadow-lg shadow-primary/25 text-base">
|
| 162 |
+
{isSignUp ? 'Create Account' : 'Sign In'}
|
| 163 |
+
</button>
|
| 164 |
+
</div>
|
| 165 |
+
|
| 166 |
+
<p className="text-center text-sm text-muted mt-8">
|
| 167 |
+
{isSignUp ? 'Already have an account?' : "Don't have an account?"}{' '}
|
| 168 |
+
<button onClick={() => setIsSignUp(!isSignUp)} className="text-primary font-medium hover:underline">{isSignUp ? 'Sign in' : 'Sign up'}</button>
|
| 169 |
+
</p>
|
| 170 |
+
|
| 171 |
+
<p className="text-center text-xs text-muted/60 mt-6">By continuing, you agree to our Terms of Service and Privacy Policy.</p>
|
| 172 |
+
</div>
|
| 173 |
+
</div>
|
| 174 |
+
</div>
|
| 175 |
+
);
|
| 176 |
+
};
|
| 177 |
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
| 178 |
+
</script>
|
| 179 |
+
</body>
|
| 180 |
+
</html>
|
workflows.html
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Workflows — NSFWStudio</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
| 9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| 10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 15 |
+
<script>
|
| 16 |
+
tailwind.config = { theme: { extend: { fontFamily: { sans: ['Inter','system-ui','sans-serif'], mono: ['JetBrains Mono','monospace'] }, colors: { bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)', fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)', secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)', destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)' } } } }
|
| 17 |
+
</script>
|
| 18 |
+
<style>
|
| 19 |
+
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
| 20 |
+
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
|
| 21 |
+
::selection { background: oklch(0.70 0.28 350 / 0.3); }
|
| 22 |
+
body::after { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat:repeat; background-size:256px 256px; }
|
| 23 |
+
.no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
| 24 |
+
</style>
|
| 25 |
+
</head>
|
| 26 |
+
<body class="antialiased">
|
| 27 |
+
<div id="root"></div>
|
| 28 |
+
<script type="text/babel">
|
| 29 |
+
const { useState } = React;
|
| 30 |
+
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
|
| 31 |
+
const ref = React.useRef(null);
|
| 32 |
+
React.useEffect(() => { if (ref.current) { const existing = ref.current.querySelector('svg'); if (existing) existing.remove(); const svg = lucide.createElement(lucide.icons[name]); if (svg) { svg.setAttribute('width',size); svg.setAttribute('height',size); svg.setAttribute('stroke-width',strokeWidth); ref.current.appendChild(svg); } } }, [name, size, className, strokeWidth]);
|
| 33 |
+
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
|
| 34 |
+
};
|
| 35 |
+
|
| 36 |
+
const navItems = [
|
| 37 |
+
{ name: 'Dashboard', icon: 'layout-dashboard', href: 'dashboard.html' },
|
| 38 |
+
{ name: 'Generator', icon: 'sparkles', href: 'generator.html' },
|
| 39 |
+
{ name: 'Playground', icon: 'code', href: 'playground.html' },
|
| 40 |
+
{ name: 'Models', icon: 'box', href: 'models.html' },
|
| 41 |
+
{ name: 'Workflows', icon: 'workflow', href: 'workflows.html', active: true },
|
| 42 |
+
{ name: 'Marketplace', icon: 'shopping-bag', href: 'marketplace.html' },
|
| 43 |
+
{ name: 'API Keys', icon: 'key', href: 'api-keys.html' },
|
| 44 |
+
{ divider: true },
|
| 45 |
+
{ name: 'Profile', icon: 'user', href: 'profile.html' },
|
| 46 |
+
{ name: 'Settings', icon: 'settings', href: 'settings.html' },
|
| 47 |
+
];
|
| 48 |
+
|
| 49 |
+
const Sidebar = ({ isOpen, setIsOpen }) => (
|
| 50 |
+
<>
|
| 51 |
+
<div className={`fixed inset-0 z-40 lg:hidden transition-all ${isOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
|
| 52 |
+
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setIsOpen(false)} />
|
| 53 |
+
</div>
|
| 54 |
+
<aside className={`fixed top-0 left-0 bottom-0 z-50 w-64 bg-surface border-r border-border flex flex-col transition-transform duration-300 lg:translate-x-0 ${isOpen ? 'translate-x-0' : '-translate-x-full'}`}>
|
| 55 |
+
<div className="p-4 flex items-center gap-2.5 border-b border-border"><div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div><span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span></div>
|
| 56 |
+
<nav className="flex-1 p-3 space-y-1 overflow-y-auto no-scrollbar">{navItems.map((item, i) => item.divider ? <div key={i} className="my-3 border-t border-border" /> : <a key={i} href={item.href} className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors ${item.active ? 'bg-primary/10 text-primary border border-primary/20' : 'text-muted hover:text-fg hover:bg-surface-2 border border-transparent'}`}><Icon name={item.icon} size={18} />{item.name}</a>)}</nav>
|
| 57 |
+
<div className="p-4 border-t border-border"><a href="documentation.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="book-open" size={18} />Documentation</a><a href="api-reference.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="file-code" size={18} />API Reference</a></div>
|
| 58 |
+
</aside>
|
| 59 |
+
</>
|
| 60 |
+
);
|
| 61 |
+
|
| 62 |
+
const App = () => {
|
| 63 |
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
| 64 |
+
const [filter, setFilter] = useState('all');
|
| 65 |
+
|
| 66 |
+
const workflows = [
|
| 67 |
+
{ name: 'Auto Tag & Categorize', desc: 'Automatically tag and categorize generated content using AI', steps: 4, runs: '12.4K', lastRun: '2 min ago', status: 'active', trigger: 'On Generation', icon: 'tag' },
|
| 68 |
+
{ name: 'Batch Upscale Pipeline', desc: 'Upscale images in batch with quality enhancement', steps: 3, runs: '8.2K', lastRun: '1 hour ago', status: 'active', trigger: 'Manual', icon: 'maximize' },
|
| 69 |
+
{ name: 'Content Moderation', desc: 'Auto-moderate content before publishing', steps: 5, runs: '45.1K', lastRun: '5 min ago', status: 'active', trigger: 'On Upload', icon: 'shield' },
|
| 70 |
+
{ name: 'Social Media Scheduler', desc: 'Generate and schedule social media content', steps: 6, runs: '3.7K', lastRun: '3 days ago', status: 'paused', trigger: 'Scheduled', icon: 'calendar' },
|
| 71 |
+
{ name: 'Model Fine-Tuner', desc: 'Automated fine-tuning workflow with dataset preparation', steps: 7, runs: '1.2K', lastRun: '1 week ago', status: 'draft', trigger: 'Manual', icon: 'cpu' },
|
| 72 |
+
{ name: 'Video Frame Extractor', desc: 'Extract and enhance key frames from video generations', steps: 3, runs: '6.8K', lastRun: '30 min ago', status: 'active', trigger: 'On Video Gen', icon: 'film' },
|
| 73 |
+
];
|
| 74 |
+
|
| 75 |
+
const filtered = filter === 'all' ? workflows : workflows.filter(w => w.status === filter);
|
| 76 |
+
const statusColors = { active: 'bg-success/10 text-success border-success/20', paused: 'bg-accent/10 text-accent border-accent/20', draft: 'bg-muted/10 text-muted border-border' };
|
| 77 |
+
|
| 78 |
+
return (
|
| 79 |
+
<div className="min-h-screen bg-bg">
|
| 80 |
+
<Sidebar isOpen={sidebarOpen} setIsOpen={setSidebarOpen} />
|
| 81 |
+
<div className="lg:ml-64">
|
| 82 |
+
<header className="sticky top-0 z-30 glass border-b border-border">
|
| 83 |
+
<div className="flex items-center justify-between px-4 sm:px-6 h-16">
|
| 84 |
+
<div className="flex items-center gap-4">
|
| 85 |
+
<button onClick={() => setSidebarOpen(true)} className="lg:hidden p-2 rounded-lg hover:bg-surface-2 text-muted"><Icon name="menu" size={20} /></button>
|
| 86 |
+
<h1 className="text-xl font-bold">Workflows</h1>
|
| 87 |
+
</div>
|
| 88 |
+
<div className="flex items-center gap-3">
|
| 89 |
+
<a href="#" className="px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-semibold hover:brightness-110 transition-all flex items-center gap-2"><Icon name="plus" size={16} />Create Workflow</a>
|
| 90 |
+
<a href="profile.html" className="w-9 h-9 rounded-lg overflow-hidden border border-border"><img src="http://static.photos/people/100x100/10" alt="" className="w-full h-full object-cover" /></a>
|
| 91 |
+
</div>
|
| 92 |
+
</div>
|
| 93 |
+
</header>
|
| 94 |
+
|
| 95 |
+
<main className="p-4 sm:p-6 lg:p-8">
|
| 96 |
+
{/* Stats */}
|
| 97 |
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
| 98 |
+
{[
|
| 99 |
+
{ label: 'Total Workflows', value: '6', icon: 'workflow', color: 'text-primary', bg: 'bg-primary/10' },
|
| 100 |
+
{ label: 'Active', value: '4', icon: 'play-circle', color: 'text-success', bg: 'bg-success/10' },
|
| 101 |
+
{ label: 'Total Runs', value: '77.4K', icon: 'activity', color: 'text-secondary', bg: 'bg-secondary/10' },
|
| 102 |
+
{ label: 'Avg Duration', value: '2.4s', icon: 'clock', color: 'text-accent', bg: 'bg-accent/10' },
|
| 103 |
+
].map((s, i) => (
|
| 104 |
+
<div key={i} className="rounded-2xl p-5 bg-surface border border-border">
|
| 105 |
+
<div className="flex items-center gap-3 mb-3">
|
| 106 |
+
<div className={`w-9 h-9 rounded-lg ${s.bg} flex items-center justify-center`}><Icon name={s.icon} size={16} className={s.color} /></div>
|
| 107 |
+
<span className="text-xs text-muted">{s.label}</span>
|
| 108 |
+
</div>
|
| 109 |
+
<div className="text-2xl font-bold text-fg">{s.value}</div>
|
| 110 |
+
</div>
|
| 111 |
+
))}
|
| 112 |
+
</div>
|
| 113 |
+
|
| 114 |
+
{/* Filters */}
|
| 115 |
+
<div className="flex items-center gap-2 mb-6">
|
| 116 |
+
{['all', 'active', 'paused', 'draft'].map(f => (
|
| 117 |
+
<button key={f} onClick={() => setFilter(f)} className={`px-4 py-2 rounded-lg text-sm font-medium capitalize transition-colors ${filter === f ? 'bg-primary text-primary-foreground' : 'text-muted hover:text-fg hover:bg-surface-2'}`}>{f}</button>
|
| 118 |
+
))}
|
| 119 |
+
</div>
|
| 120 |
+
|
| 121 |
+
{/* Workflow list */}
|
| 122 |
+
<div className="space-y-4">
|
| 123 |
+
{filtered.map((w, i) => (
|
| 124 |
+
<div key={i} className="group rounded-2xl p-5 bg-surface border border-border hover:border-surface-3 transition-all">
|
| 125 |
+
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
| 126 |
+
<div className="flex items-start gap-4">
|
| 127 |
+
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center shrink-0"><Icon name={w.icon} size={22} className="text-primary" /></div>
|
| 128 |
+
<div>
|
| 129 |
+
<div className="flex items-center gap-2 mb-1">
|
| 130 |
+
<h3 className="font-semibold text-fg">{w.name}</h3>
|
| 131 |
+
<span className={`px-2.5 py-0.5 rounded-full text-xs font-semibold border ${statusColors[w.status]}`}>{w.status}</span>
|
| 132 |
+
</div>
|
| 133 |
+
<p className="text-sm text-muted mb-2">{w.desc}</p>
|
| 134 |
+
<div className="flex items-center gap-4 text-xs text-muted">
|
| 135 |
+
<span className="flex items-center gap-1"><Icon name="git-branch" size={12} />{w.steps} steps</span>
|
| 136 |
+
<span className="flex items-center gap-1"><Icon name="play" size={12} />{w.runs} runs</span>
|
| 137 |
+
<span className="flex items-center gap-1"><Icon name="zap" size={12} />{w.trigger}</span>
|
| 138 |
+
</div>
|
| 139 |
+
</div>
|
| 140 |
+
</div>
|
| 141 |
+
<div className="flex items-center gap-2">
|
| 142 |
+
<button className="px-3 py-2 rounded-lg border border-border text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors flex items-center gap-1.5"><Icon name="play" size={14} />Run</button>
|
| 143 |
+
<button className="px-3 py-2 rounded-lg border border-border text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors flex items-center gap-1.5"><Icon name="pencil" size={14} />Edit</button>
|
| 144 |
+
<button className="p-2 rounded-lg border border-border text-muted hover:text-fg hover:bg-surface-2 transition-colors"><Icon name="more-vertical" size={16} /></button>
|
| 145 |
+
</div>
|
| 146 |
+
</div>
|
| 147 |
+
|
| 148 |
+
{/* Mini flow visualization */}
|
| 149 |
+
<div className="mt-4 flex items-center gap-2 overflow-x-auto no-scrollbar pb-1">
|
| 150 |
+
{Array.from({length: w.steps}).map((_, j) => (
|
| 151 |
+
<React.Fragment key={j}>
|
| 152 |
+
<div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-surface-2 border border-border shrink-0">
|
| 153 |
+
<div className={`w-2 h-2 rounded-full ${j < 3 ? 'bg-success' : j === 3 ? 'bg-primary' : 'bg-muted'}`} />
|
| 154 |
+
<span className="text-xs text-muted">Step {j + 1}</span>
|
| 155 |
+
</div>
|
| 156 |
+
{j < w.steps - 1 && <Icon name="chevron-right" size={14} className="text-muted shrink-0" />}
|
| 157 |
+
</React.Fragment>
|
| 158 |
+
))}
|
| 159 |
+
</div>
|
| 160 |
+
</div>
|
| 161 |
+
))}
|
| 162 |
+
</div>
|
| 163 |
+
</main>
|
| 164 |
+
</div>
|
| 165 |
+
</div>
|
| 166 |
+
);
|
| 167 |
+
};
|
| 168 |
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
| 169 |
+
</script>
|
| 170 |
+
</body>
|
| 171 |
+
</html>
|