feat: flip k and z horizontally for glitchy aesthetic
Change vertical to horizontal flip for 'k' and 'z'. #VERCEL_SKIP Co-authored-by: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
This commit is contained in:
parent
ea35500a0d
commit
6b58b1e8ac
|
|
@ -0,0 +1,46 @@
|
|||
# Cloudflare Pages Deployment Guide
|
||||
|
||||
## Static Export Configuration
|
||||
|
||||
This project is configured to build as a static export with `output: 'export'` in `next.config.mjs`. When you run `npm run build`, it will generate static files in the `/out` directory.
|
||||
|
||||
## Cloudflare Pages Function (Waitlist API)
|
||||
|
||||
The waitlist functionality uses a Cloudflare Pages Function located at `functions/api/waitlist.ts`. This will run serverlessly on Cloudflare's edge network.
|
||||
|
||||
### Setting up KV Namespace
|
||||
|
||||
The waitlist function requires a Cloudflare KV namespace to store emails:
|
||||
|
||||
1. In your Cloudflare dashboard, go to Workers & Pages > KV
|
||||
2. Create a new KV namespace called `WAITLIST`
|
||||
3. In your Pages project settings, go to Settings > Functions > KV namespace bindings
|
||||
4. Add a binding:
|
||||
- Variable name: `WAITLIST`
|
||||
- KV namespace: Select the `WAITLIST` namespace you created
|
||||
|
||||
### Build Configuration
|
||||
|
||||
Cloudflare Pages will automatically detect the Next.js static export. Use these settings:
|
||||
|
||||
- **Build command**: `npm run build`
|
||||
- **Build output directory**: `out`
|
||||
- **Node version**: 18 or higher
|
||||
|
||||
### Deployment
|
||||
|
||||
1. Connect your GitHub repository to Cloudflare Pages
|
||||
2. Configure the build settings as above
|
||||
3. Add the KV namespace binding
|
||||
4. Deploy!
|
||||
|
||||
The site will be served statically from the `/out` directory, and the `/api/waitlist` endpoint will be handled by the Cloudflare Pages Function.
|
||||
|
||||
## Alternative: Simple Form Submission
|
||||
|
||||
If you prefer not to use Cloudflare KV, you can also use:
|
||||
- A third-party service like Formspree, Tally, or Airtable
|
||||
- A simple mailto link or Google Form
|
||||
- Any webhook service
|
||||
|
||||
Simply update the `handleSubmit` function in `components/waitlist-section.tsx` to point to your chosen service.
|
||||
|
|
@ -27,4 +27,4 @@ Continue building your app on:
|
|||
1. Create and modify your project using [v0.app](https://v0.app)
|
||||
2. Deploy your chats from the v0 interface
|
||||
3. Changes are automatically pushed to this repository
|
||||
4. Vercel deploys the latest version from this repository
|
||||
4. Vercel deploys the latest version from this repository
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
import { NextResponse } from 'next/server'
|
||||
|
||||
// This is a simple in-memory store for demo purposes
|
||||
// In production, you'd want to use a database
|
||||
const waitlist = new Set<string>()
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { email } = await request.json()
|
||||
|
||||
if (!email || !email.includes('@')) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Valid email required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
if (waitlist.has(email)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Already in the network' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
waitlist.add(email)
|
||||
|
||||
// In production, you'd save to a database here
|
||||
// and potentially send a confirmation email
|
||||
console.log('[v0] New waitlist signup:', email)
|
||||
console.log('[v0] Total waitlist size:', waitlist.size)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Welcome to the underground',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('[v0] Waitlist error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Network error occurred' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: Add a GET endpoint to check waitlist status (for admin use)
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
count: waitlist.size,
|
||||
})
|
||||
}
|
||||
|
|
@ -30,7 +30,9 @@ export function HeroSection() {
|
|||
</div>
|
||||
|
||||
<h1 className="text-5xl md:text-7xl lg:text-8xl font-bold mb-8 font-mono mycelium-glow">
|
||||
<span className="block text-balance">myc0punkz</span>
|
||||
<span className="block text-balance">
|
||||
myc0pun<span className="inline-block scale-x-[-1]">k</span><span className="inline-block scale-x-[-1]">z</span>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<div className="text-lg md:text-xl text-muted-foreground mb-12 max-w-2xl mx-auto font-mono leading-relaxed">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
// Cloudflare Pages Function for waitlist
|
||||
// This will run on Cloudflare's edge network
|
||||
|
||||
interface Env {
|
||||
WAITLIST: KVNamespace
|
||||
}
|
||||
|
||||
export async function onRequestPost(context: { request: Request; env: Env }) {
|
||||
try {
|
||||
const { email } = await context.request.json()
|
||||
|
||||
if (!email || !email.includes('@')) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Valid email required' }),
|
||||
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
}
|
||||
|
||||
// Check if email already exists using Cloudflare KV
|
||||
const existing = await context.env.WAITLIST?.get(email)
|
||||
|
||||
if (existing) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Already in the network' }),
|
||||
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
}
|
||||
|
||||
// Store email with timestamp in KV
|
||||
await context.env.WAITLIST?.put(email, JSON.stringify({
|
||||
email,
|
||||
timestamp: new Date().toISOString(),
|
||||
}))
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: 'Welcome to the underground',
|
||||
}),
|
||||
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
} catch (error) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Network error occurred' }),
|
||||
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: GET endpoint to check waitlist count
|
||||
export async function onRequestGet(context: { env: Env }) {
|
||||
try {
|
||||
const list = await context.env.WAITLIST?.list()
|
||||
return new Response(
|
||||
JSON.stringify({ count: list?.keys.length || 0 }),
|
||||
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
} catch (error) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Network error' }),
|
||||
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: 'export',
|
||||
distDir: 'out',
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
export default nextConfig
|
||||
export default nextConfig
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
"@radix-ui/react-select": "2.1.4",
|
||||
"@radix-ui/react-separator": "1.1.1",
|
||||
"@radix-ui/react-slider": "1.2.2",
|
||||
"@radix-ui/react-slot": "1.1.1",
|
||||
"@radix-ui/react-slot": "latest",
|
||||
"@radix-ui/react-switch": "1.1.2",
|
||||
"@radix-ui/react-tabs": "1.1.2",
|
||||
"@radix-ui/react-toast": "1.2.4",
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
"input-otp": "1.4.1",
|
||||
"lucide-react": "^0.454.0",
|
||||
"next": "16.0.3",
|
||||
"next-themes": "^0.4.6",
|
||||
"next-themes": "latest",
|
||||
"react": "19.2.0",
|
||||
"react-day-picker": "9.8.0",
|
||||
"react-dom": "19.2.0",
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ importers:
|
|||
specifier: 1.2.2
|
||||
version: 1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-slot':
|
||||
specifier: 1.1.1
|
||||
version: 1.1.1(@types/react@19.0.0)(react@19.2.0)
|
||||
specifier: latest
|
||||
version: 1.2.4(@types/react@19.0.0)(react@19.2.0)
|
||||
'@radix-ui/react-switch':
|
||||
specifier: 1.1.2
|
||||
version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
|
|
@ -123,7 +123,7 @@ importers:
|
|||
specifier: 16.0.3
|
||||
version: 16.0.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
next-themes:
|
||||
specifier: ^0.4.6
|
||||
specifier: latest
|
||||
version: 0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
react:
|
||||
specifier: 19.2.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue