mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-15 04:40:40 -07:00
Replace sponsor program with $VOICEBOX token
- Remove the sponsor feature (sponsors page, homepage promo, footer link, Stripe constants, and the app About-page sponsored-by section) - Add $VOICEBOX token: navbar pill linking to pump.fun and a footer Token column with a copyable Solana contract address - Drop API and Download from the navbar links
This commit is contained in:
@@ -3,7 +3,6 @@ import type { CSSProperties, ReactNode } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import voiceboxLogo from '@/assets/voicebox-logo.png';
|
||||
import { SPONSORS } from '@/lib/sponsors';
|
||||
import { usePlatform } from '@/platform/PlatformContext';
|
||||
|
||||
function FadeIn({ delay = 0, children }: { delay?: number; children: ReactNode }) {
|
||||
@@ -117,36 +116,6 @@ export function AboutPage() {
|
||||
</div>
|
||||
</FadeIn>
|
||||
|
||||
{SPONSORS.length > 0 && (
|
||||
<FadeIn delay={400}>
|
||||
<div className="pt-4 flex flex-col items-center gap-3">
|
||||
<p className="text-[10px] font-semibold uppercase tracking-[0.22em] text-muted-foreground/60">
|
||||
Sponsored by
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center justify-center gap-3">
|
||||
{SPONSORS.map((sponsor) => (
|
||||
<a
|
||||
key={sponsor.name}
|
||||
href={sponsor.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={sponsor.name}
|
||||
className="group flex h-12 min-w-[120px] items-center justify-center rounded-lg border border-border/60 bg-card/50 px-4 transition-colors hover:bg-muted/50"
|
||||
>
|
||||
<img
|
||||
src={sponsor.logoSrc}
|
||||
alt={sponsor.logoAlt ?? sponsor.name}
|
||||
className={`h-5 w-auto max-w-[100px] object-contain opacity-80 transition-opacity group-hover:opacity-100 ${
|
||||
sponsor.invertOnDark ? 'dark:brightness-0 dark:invert' : ''
|
||||
}`}
|
||||
/>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
)}
|
||||
|
||||
<FadeIn delay={480}>
|
||||
<p className="text-xs text-muted-foreground/40 pt-4">
|
||||
<Trans
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
export type Sponsor = {
|
||||
name: string;
|
||||
url: string;
|
||||
logoSrc: string;
|
||||
logoAlt?: string;
|
||||
/** Set true for solid-black logos that need to flip white in dark mode. */
|
||||
invertOnDark?: boolean;
|
||||
};
|
||||
|
||||
export const SPONSORS: Sponsor[] = [];
|
||||
@@ -1 +0,0 @@
|
||||
<svg viewBox="0 0 1180 320" xmlns="http://www.w3.org/2000/svg"><path d="m367.44 153.84c0 52.32 33.6 88.8 80.16 88.8s80.16-36.48 80.16-88.8-33.6-88.8-80.16-88.8-80.16 36.48-80.16 88.8zm129.6 0c0 37.44-20.4 61.68-49.44 61.68s-49.44-24.24-49.44-61.68 20.4-61.68 49.44-61.68 49.44 24.24 49.44 61.68z"/><path d="m614.27 242.64c35.28 0 55.44-29.76 55.44-65.52s-20.16-65.52-55.44-65.52c-16.32 0-28.32 6.48-36.24 15.84v-13.44h-28.8v169.2h28.8v-56.4c7.92 9.36 19.92 15.84 36.24 15.84zm-36.96-69.12c0-23.76 13.44-36.72 31.2-36.72 20.88 0 32.16 16.32 32.16 40.32s-11.28 40.32-32.16 40.32c-17.76 0-31.2-13.2-31.2-36.48z"/><path d="m747.65 242.64c25.2 0 45.12-13.2 54-35.28l-24.72-9.36c-3.84 12.96-15.12 20.16-29.28 20.16-18.48 0-31.44-13.2-33.6-34.8h88.32v-9.6c0-34.56-19.44-62.16-55.92-62.16s-60 28.56-60 65.52c0 38.88 25.2 65.52 61.2 65.52zm-1.44-106.8c18.24 0 26.88 12 27.12 25.92h-57.84c4.32-17.04 15.84-25.92 30.72-25.92z"/><path d="m823.98 240h28.8v-73.92c0-18 13.2-27.6 26.16-27.6 15.84 0 22.08 11.28 22.08 26.88v74.64h28.8v-83.04c0-27.12-15.84-45.36-42.24-45.36-16.32 0-27.6 7.44-34.8 15.84v-13.44h-28.8z"/><path d="m1014.17 67.68-65.28 172.32h30.48l14.64-39.36h74.4l14.88 39.36h30.96l-65.28-172.32zm16.8 34.08 27.36 72h-54.24z"/><path d="m1163.69 68.18h-30.72v172.32h30.72z"/><path d="m297.06 130.97c7.26-21.79 4.76-45.66-6.85-65.48-17.46-30.4-52.56-46.04-86.84-38.68-15.25-17.18-37.16-26.95-60.13-26.81-35.04-.08-66.13 22.48-76.91 55.82-22.51 4.61-41.94 18.7-53.31 38.67-17.59 30.32-13.58 68.54 9.92 94.54-7.26 21.79-4.76 45.66 6.85 65.48 17.46 30.4 52.56 46.04 86.84 38.68 15.24 17.18 37.16 26.95 60.13 26.8 35.06.09 66.16-22.49 76.94-55.86 22.51-4.61 41.94-18.7 53.31-38.67 17.57-30.32 13.55-68.51-9.94-94.51zm-120.28 168.11c-14.03.02-27.62-4.89-38.39-13.88.49-.26 1.34-.73 1.89-1.07l63.72-36.8c3.26-1.85 5.26-5.32 5.24-9.07v-89.83l26.93 15.55c.29.14.48.42.52.74v74.39c-.04 33.08-26.83 59.9-59.91 59.97zm-128.84-55.03c-7.03-12.14-9.56-26.37-7.15-40.18.47.28 1.3.79 1.89 1.13l63.72 36.8c3.23 1.89 7.23 1.89 10.47 0l77.79-44.92v31.1c.02.32-.13.63-.38.83l-64.41 37.19c-28.69 16.52-65.33 6.7-81.92-21.95zm-16.77-139.09c7-12.16 18.05-21.46 31.21-26.29 0 .55-.03 1.52-.03 2.2v73.61c-.02 3.74 1.98 7.21 5.23 9.06l77.79 44.91-26.93 15.55c-.27.18-.61.21-.91.08l-64.42-37.22c-28.63-16.58-38.45-53.21-21.95-81.89zm221.26 51.49-77.79-44.92 26.93-15.54c.27-.18.61-.21.91-.08l64.42 37.19c28.68 16.57 38.51 53.26 21.94 81.94-7.01 12.14-18.05 21.44-31.2 26.28v-75.81c.03-3.74-1.96-7.2-5.2-9.06zm26.8-40.34c-.47-.29-1.3-.79-1.89-1.13l-63.72-36.8c-3.23-1.89-7.23-1.89-10.47 0l-77.79 44.92v-31.1c-.02-.32.13-.63.38-.83l64.41-37.16c28.69-16.55 65.37-6.7 81.91 22 6.99 12.12 9.52 26.31 7.15 40.1zm-168.51 55.43-26.94-15.55c-.29-.14-.48-.42-.52-.74v-74.39c.02-33.12 26.89-59.96 60.01-59.94 14.01 0 27.57 4.92 38.34 13.88-.49.26-1.33.73-1.89 1.07l-63.72 36.8c-3.26 1.85-5.26 5.31-5.24 9.06l-.04 89.79zm14.63-31.54 34.65-20.01 34.65 20v40.01l-34.65 20-34.65-20z"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -11,7 +11,6 @@ import {Footer} from "@/components/Footer";
|
||||
import {Navbar} from "@/components/Navbar";
|
||||
import {Personalities} from "@/components/Personalities";
|
||||
import {AppleIcon, LinuxIcon, WindowsIcon} from "@/components/PlatformIcons";
|
||||
import {SponsorPromo} from "@/components/SponsorPromo";
|
||||
import {SupportedModels} from "@/components/SupportedModels";
|
||||
import {TutorialsSection} from "@/components/TutorialsSection";
|
||||
import {VoiceCreator} from "@/components/VoiceCreator";
|
||||
@@ -131,9 +130,6 @@ export default function Home() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Sponsor promo ────────────────────────────────────────── */}
|
||||
<SponsorPromo />
|
||||
|
||||
{/* ── Features ─────────────────────────────────────────────── */}
|
||||
<Features />
|
||||
|
||||
|
||||
@@ -1,324 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { ArrowRight, Check, Coffee, Mail } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Footer } from '@/components/Footer';
|
||||
import { Navbar } from '@/components/Navbar';
|
||||
import {
|
||||
DONATE_URL,
|
||||
SPONSOR_CHECKOUT_URL,
|
||||
SPONSOR_CONTACT_EMAIL,
|
||||
} from '@/lib/constants';
|
||||
|
||||
function formatCount(n: number): string {
|
||||
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`;
|
||||
if (n >= 1_000) return `${(n / 1_000).toFixed(0)}k`;
|
||||
return n.toLocaleString();
|
||||
}
|
||||
|
||||
export default function SponsorsPage() {
|
||||
const [downloads, setDownloads] = useState<number | null>(null);
|
||||
const [stars, setStars] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api/releases')
|
||||
.then((res) => (res.ok ? res.json() : null))
|
||||
.then((data) => {
|
||||
if (data?.totalDownloads != null) setDownloads(data.totalDownloads);
|
||||
})
|
||||
.catch(() => {});
|
||||
fetch('/api/stars')
|
||||
.then((res) => (res.ok ? res.json() : null))
|
||||
.then((data) => {
|
||||
if (typeof data?.count === 'number') setStars(data.count);
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
|
||||
{/* ── Hero ─────────────────────────────────────────────────── */}
|
||||
<section className="relative pt-32 pb-16">
|
||||
<div className="hero-glow hero-glow-fade pointer-events-none absolute inset-0 -top-32">
|
||||
<div className="absolute left-1/2 top-0 -translate-x-1/2 w-[900px] h-[500px] rounded-full bg-accent/12 blur-[140px]" />
|
||||
<div className="absolute left-1/2 top-16 -translate-x-1/2 w-[520px] h-[360px] rounded-full bg-accent/8 blur-[80px]" />
|
||||
</div>
|
||||
|
||||
<div className="relative mx-auto max-w-4xl px-6 text-center">
|
||||
<div
|
||||
className="fade-in mb-6 text-[11px] font-semibold uppercase tracking-[0.22em] text-accent"
|
||||
style={{ animationDelay: '50ms' }}
|
||||
>
|
||||
VIP Sponsor
|
||||
</div>
|
||||
|
||||
<h1
|
||||
className="fade-in text-5xl font-bold tracking-tighter leading-[0.95] text-foreground md:text-6xl lg:text-7xl"
|
||||
style={{ animationDelay: '100ms' }}
|
||||
>
|
||||
Get your brand in front of half a million creators.
|
||||
</h1>
|
||||
|
||||
<p
|
||||
className="fade-in mx-auto mt-6 max-w-2xl text-lg text-muted-foreground md:text-xl"
|
||||
style={{ animationDelay: '200ms' }}
|
||||
>
|
||||
Voicebox is the open-source AI voice studio used by creators, podcasters, voice
|
||||
artists, writers, developers, accessibility users, and curious humans all over the
|
||||
world. Sponsor the project, get your logo in front of all of them.
|
||||
</p>
|
||||
|
||||
<div
|
||||
className="fade-in mt-10 flex flex-row items-center justify-center gap-3 sm:gap-4"
|
||||
style={{ animationDelay: '300ms' }}
|
||||
>
|
||||
<a
|
||||
href="#sponsor"
|
||||
className="rounded-full bg-accent px-8 py-3.5 text-sm font-semibold uppercase tracking-wider text-white shadow-[0_4px_20px_hsl(43_60%_50%/0.3),inset_0_2px_0_rgba(255,255,255,0.2),inset_0_-2px_0_rgba(0,0,0,0.1)] transition-all hover:bg-accent-faint active:shadow-[0_2px_10px_hsl(43_60%_50%/0.3),inset_0_4px_8px_rgba(0,0,0,0.3)]"
|
||||
>
|
||||
Become a sponsor
|
||||
</a>
|
||||
<a
|
||||
href={`mailto:${SPONSOR_CONTACT_EMAIL}`}
|
||||
className="flex items-center gap-2 rounded-full border border-border/60 bg-card/40 backdrop-blur-sm px-6 py-3 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground hover:border-border"
|
||||
>
|
||||
<Mail className="h-4 w-4" />
|
||||
Talk to us
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Traction ────────────────────────────────────────────── */}
|
||||
<section className="border-t border-border py-20">
|
||||
<div className="mx-auto max-w-5xl px-6">
|
||||
<div className="text-center mb-12">
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-accent mb-4">
|
||||
Reach
|
||||
</div>
|
||||
<h2 className="text-3xl md:text-4xl font-semibold tracking-tight text-foreground">
|
||||
Real distribution, real attention.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<Stat
|
||||
value={downloads != null ? formatCount(downloads) : '500k+'}
|
||||
label="Downloads"
|
||||
note="Since launch in February 2026"
|
||||
/>
|
||||
<Stat
|
||||
value={stars != null ? formatCount(stars) : '22k+'}
|
||||
label="GitHub stars"
|
||||
note="Trending #1 maintainer, #4 repo"
|
||||
/>
|
||||
<Stat
|
||||
value="170k+"
|
||||
label="Monthly site visitors"
|
||||
note="voicebox.sh, last 30 days · growing 5×"
|
||||
/>
|
||||
<Stat
|
||||
value="Millions"
|
||||
label="Social reach"
|
||||
note="Tutorials, reels, TikToks"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-center text-sm text-muted-foreground mt-10 max-w-2xl mx-auto">
|
||||
Voicebox users are content creators, podcasters, voice artists, writers, developers,
|
||||
accessibility users, hobbyists, and AI enthusiasts. They picked a local-first tool
|
||||
over a cloud subscription — they care about owning their software and the brands
|
||||
behind it.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── What you get ────────────────────────────────────────── */}
|
||||
<section className="border-t border-border py-20">
|
||||
<div className="mx-auto max-w-5xl px-6">
|
||||
<div className="text-center mb-12">
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-accent mb-4">
|
||||
Placement
|
||||
</div>
|
||||
<h2 className="text-3xl md:text-4xl font-semibold tracking-tight text-foreground">
|
||||
Where your logo shows up.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border-2 border-accent/40 bg-card/60 backdrop-blur-sm p-8 mb-4 shadow-[0_8px_40px_hsl(43_60%_50%/0.08)]">
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-accent mb-2">
|
||||
Headline placement
|
||||
</div>
|
||||
<h3 className="text-xl md:text-2xl font-semibold tracking-tight text-foreground mb-2">
|
||||
voicebox.sh — directly below the hero.
|
||||
</h3>
|
||||
<p className="text-sm md:text-base text-muted-foreground leading-relaxed max-w-2xl mb-8">
|
||||
Your logo lives in the prime slot on the homepage — the first thing every visitor
|
||||
sees after the hero. Same row as every other VIP Sponsor, linked to your URL
|
||||
of choice. This is the placement that actually moves the needle.
|
||||
</p>
|
||||
|
||||
{/* Preview of what the placement looks like on the homepage */}
|
||||
<div className="rounded-xl border border-dashed border-border/80 bg-background/50 p-6 md:p-8">
|
||||
<div className="text-center mb-6">
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground/80">
|
||||
Sponsored by
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center justify-center gap-5 md:gap-6">
|
||||
<div className="flex h-32 min-w-[260px] items-center justify-center rounded-2xl border border-border bg-card/40 backdrop-blur-sm px-10">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src="/sponsors/openai.svg"
|
||||
alt="Example sponsor logo"
|
||||
className="h-14 w-auto max-w-[220px] object-contain brightness-0 invert opacity-80"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-6 text-center text-xs text-muted-foreground/70 italic">
|
||||
Example only — actual sponsor logos appear here once placements are live.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-4">
|
||||
<Perk
|
||||
title="GitHub README"
|
||||
body="Logo in the repo's Sponsors section. The README is one of the most-viewed docs on GitHub for any trending project."
|
||||
/>
|
||||
<Perk
|
||||
title="/sponsors page"
|
||||
body="Dedicated logo card on this page with your tagline, what your company does, and a direct link out."
|
||||
/>
|
||||
<Perk
|
||||
title="Release notes"
|
||||
body="One-line acknowledgement in the next major release post — read by the long tail of users who follow Voicebox updates."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Pricing ─────────────────────────────────────────────── */}
|
||||
<section id="sponsor" className="border-t border-border py-20">
|
||||
<div className="mx-auto max-w-3xl px-6">
|
||||
<div className="text-center mb-10">
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-accent mb-4">
|
||||
Pricing
|
||||
</div>
|
||||
<h2 className="text-3xl md:text-4xl font-semibold tracking-tight text-foreground">
|
||||
One tier. Month-to-month. Cancel anytime.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border-2 border-accent/40 bg-card/60 backdrop-blur-sm p-8 md:p-10 shadow-[0_8px_40px_hsl(43_60%_50%/0.1)]">
|
||||
<div className="flex items-baseline gap-2 mb-2">
|
||||
<span className="text-5xl font-bold tracking-tight text-foreground">$500</span>
|
||||
<span className="text-base text-muted-foreground">/ month</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mb-8">
|
||||
Billed monthly via Stripe. Logo goes live within 48 hours of payment.
|
||||
</p>
|
||||
|
||||
<ul className="space-y-3 mb-8">
|
||||
<PerkRow text="Logo on voicebox.sh — directly below the hero" />
|
||||
<PerkRow text="Logo in the GitHub README sponsors section" />
|
||||
<PerkRow text="Featured card on /sponsors with your tagline and link" />
|
||||
<PerkRow text="Acknowledgement in the next major release post" />
|
||||
<PerkRow text="Direct line to the team for collaboration" />
|
||||
</ul>
|
||||
|
||||
<a
|
||||
href={SPONSOR_CHECKOUT_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 w-full rounded-full bg-accent px-6 py-3.5 text-sm font-semibold uppercase tracking-wider text-white shadow-[0_4px_20px_hsl(43_60%_50%/0.3),inset_0_2px_0_rgba(255,255,255,0.2),inset_0_-2px_0_rgba(0,0,0,0.1)] transition-all hover:bg-accent-faint"
|
||||
>
|
||||
Sponsor Voicebox
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</a>
|
||||
|
||||
<p className="text-center text-xs text-muted-foreground/70 mt-4">
|
||||
Need an annual contract, invoicing, or higher placement?{' '}
|
||||
<a
|
||||
href={`mailto:${SPONSOR_CONTACT_EMAIL}`}
|
||||
className="text-foreground/80 underline-offset-4 hover:underline"
|
||||
>
|
||||
{SPONSOR_CONTACT_EMAIL}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Individual / policy ─────────────────────────────────── */}
|
||||
<section className="border-t border-border py-20">
|
||||
<div className="mx-auto max-w-4xl px-6 grid md:grid-cols-2 gap-4">
|
||||
<div className="rounded-xl border border-border bg-card/40 backdrop-blur-sm p-6">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Coffee className="h-5 w-5 text-[#FFDD00]" />
|
||||
<h3 className="text-[15px] font-semibold text-foreground">Not a company?</h3>
|
||||
</div>
|
||||
<p className="text-sm leading-relaxed text-muted-foreground mb-4">
|
||||
Individual supporters keep Voicebox running too. Drop a tip on Buy Me a Coffee and
|
||||
your name shows up in the supporters list.
|
||||
</p>
|
||||
<a
|
||||
href={DONATE_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 text-sm font-medium text-foreground/80 hover:text-foreground transition-colors"
|
||||
>
|
||||
Support on Buy Me a Coffee
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border border-border bg-card/40 backdrop-blur-sm p-6">
|
||||
<h3 className="text-[15px] font-semibold text-foreground mb-3">
|
||||
Who we accept
|
||||
</h3>
|
||||
<p className="text-sm leading-relaxed text-muted-foreground">
|
||||
Voicebox is local-first and privacy-first. We don't accept sponsorships from
|
||||
companies whose business model conflicts with that — voice-data brokers, ad-tech
|
||||
built on speech, or surveillance vendors. Everyone else is welcome.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Stat({ value, label, note }: { value: string; label: string; note: string }) {
|
||||
return (
|
||||
<div className="rounded-xl border border-border bg-card/40 backdrop-blur-sm p-5 text-center">
|
||||
<div className="text-3xl md:text-4xl font-bold tracking-tight text-foreground">{value}</div>
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-accent mt-2">
|
||||
{label}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-2">{note}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Perk({ title, body }: { title: string; body: string }) {
|
||||
return (
|
||||
<div className="rounded-xl border border-border bg-card/40 backdrop-blur-sm p-6">
|
||||
<h3 className="text-[15px] font-semibold text-foreground mb-2">{title}</h3>
|
||||
<p className="text-sm leading-relaxed text-muted-foreground">{body}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PerkRow({ text }: { text: string }) {
|
||||
return (
|
||||
<li className="flex items-start gap-3 text-sm text-foreground/90">
|
||||
<Check className="h-5 w-5 shrink-0 text-accent mt-px" />
|
||||
<span>{text}</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { Check, Copy } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
/** Compact, copyable contract address — short display, full value to clipboard. */
|
||||
export function CopyAddress({ address }: { address: string }) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(address);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch {
|
||||
// Clipboard unavailable (e.g. insecure context) — silently no-op.
|
||||
}
|
||||
};
|
||||
|
||||
const short = `${address.slice(0, 4)}…${address.slice(-4)}`;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopy}
|
||||
title={address}
|
||||
aria-label={copied ? 'Contract address copied' : 'Copy contract address'}
|
||||
className="group inline-flex items-center gap-2 rounded-lg border border-border/60 bg-card/60 px-2.5 py-1.5 font-mono text-xs text-muted-foreground transition-colors hover:border-border hover:text-foreground"
|
||||
>
|
||||
<span>{short}</span>
|
||||
{copied ? (
|
||||
<Check className="h-3.5 w-3.5 text-accent" />
|
||||
) : (
|
||||
<Copy className="h-3.5 w-3.5 opacity-60 transition-opacity group-hover:opacity-100" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,20 @@
|
||||
import { Coffee } from 'lucide-react';
|
||||
import { ArrowUpRight, Coffee, Coins } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { DONATE_URL, GITHUB_REPO } from '@/lib/constants';
|
||||
import { CopyAddress } from '@/components/CopyAddress';
|
||||
import {
|
||||
DONATE_URL,
|
||||
GITHUB_REPO,
|
||||
TOKEN_CONTRACT_ADDRESS,
|
||||
TOKEN_PUMP_URL,
|
||||
TOKEN_TICKER,
|
||||
} from '@/lib/constants';
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="border-t border-border py-12">
|
||||
<div className="mx-auto max-w-7xl px-6">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-8 mb-10">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-5 gap-8 mb-10">
|
||||
{/* Brand */}
|
||||
<div className="md:col-span-1">
|
||||
<div className="flex items-center gap-2.5 mb-4">
|
||||
@@ -116,11 +123,6 @@ export function Footer() {
|
||||
Issues
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/sponsors" className="hover:text-foreground transition-colors">
|
||||
VIP Sponsor
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -150,6 +152,28 @@ export function Footer() {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Token */}
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold mb-3">Token</h4>
|
||||
<div className="space-y-3 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-2">
|
||||
<Coins className="h-4 w-4 text-accent" />
|
||||
<span className="font-semibold text-foreground">{TOKEN_TICKER}</span>
|
||||
<span className="text-xs text-muted-foreground/60">Solana</span>
|
||||
</div>
|
||||
<CopyAddress address={TOKEN_CONTRACT_ADDRESS} />
|
||||
<a
|
||||
href={TOKEN_PUMP_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 hover:text-foreground transition-colors"
|
||||
>
|
||||
Buy on pump.fun
|
||||
<ArrowUpRight className="h-3.5 w-3.5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-border pt-6">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { Coffee, Github } from 'lucide-react';
|
||||
import { Coffee, Coins, Github } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DONATE_URL, GITHUB_REPO } from '@/lib/constants';
|
||||
import { DONATE_URL, GITHUB_REPO, TOKEN_PUMP_URL, TOKEN_TICKER } from '@/lib/constants';
|
||||
|
||||
function formatStarCount(count: number): string {
|
||||
if (count >= 1000) {
|
||||
@@ -74,18 +74,6 @@ export function Navbar() {
|
||||
>
|
||||
Models
|
||||
</a>
|
||||
<a
|
||||
href="/#api"
|
||||
className="rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<a
|
||||
href="/download"
|
||||
className="rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
<a
|
||||
href="https://docs.voicebox.sh"
|
||||
target="_blank"
|
||||
@@ -96,8 +84,20 @@ export function Navbar() {
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Donate + GitHub star buttons */}
|
||||
{/* Token + Donate + GitHub star buttons */}
|
||||
<div className="flex items-center gap-2 justify-self-end">
|
||||
<a
|
||||
href={TOKEN_PUMP_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hidden sm:flex items-center gap-2 rounded-lg border border-border/60 bg-card/60 px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground hover:border-accent/40"
|
||||
aria-label={`${TOKEN_TICKER} token on pump.fun`}
|
||||
>
|
||||
<Coins className="h-4 w-4 text-accent" />
|
||||
<span className="text-[13px] font-semibold tracking-wide text-foreground">
|
||||
{TOKEN_TICKER}
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href={DONATE_URL}
|
||||
target="_blank"
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import { ArrowRight, Heart } from 'lucide-react';
|
||||
import { SPONSORS, type Sponsor } from '@/lib/sponsors';
|
||||
|
||||
export function SponsorPromo() {
|
||||
if (SPONSORS.length === 0) {
|
||||
return <SponsorPromoEmpty />;
|
||||
}
|
||||
return <SponsorStrip sponsors={SPONSORS} />;
|
||||
}
|
||||
|
||||
function SponsorPromoEmpty() {
|
||||
return (
|
||||
<section className="border-t border-border py-16">
|
||||
<div className="mx-auto max-w-5xl px-6">
|
||||
<div className="rounded-2xl border-2 border-accent/40 bg-gradient-to-br from-card/80 to-card/40 backdrop-blur-sm p-8 md:p-10 shadow-[0_8px_40px_hsl(43_60%_50%/0.08)]">
|
||||
<div className="grid md:grid-cols-[1fr_auto] items-center gap-8">
|
||||
<div>
|
||||
<div className="inline-flex items-center gap-2 mb-4 text-[11px] font-semibold uppercase tracking-[0.22em] text-accent">
|
||||
<Heart className="h-3.5 w-3.5" />
|
||||
Sponsor Voicebox
|
||||
</div>
|
||||
<h3 className="text-2xl md:text-3xl font-semibold tracking-tight text-foreground mb-3">
|
||||
Get your logo in front of 170k+ monthly visitors.
|
||||
</h3>
|
||||
<p className="text-sm md:text-base text-muted-foreground leading-relaxed max-w-2xl">
|
||||
Voicebox is open-source and used by creators, voice artists, podcasters,
|
||||
writers, developers, accessibility users, and curious humans all over the world.
|
||||
Sponsor the project and your logo lands on the homepage, in the app, in the
|
||||
README, and on the sponsors page — in front of every one of them.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start md:items-end gap-2">
|
||||
<a
|
||||
href="/sponsors"
|
||||
className="inline-flex items-center gap-2 rounded-full bg-accent px-6 py-3 text-sm font-semibold uppercase tracking-wider text-white shadow-[0_4px_20px_hsl(43_60%_50%/0.3),inset_0_2px_0_rgba(255,255,255,0.2),inset_0_-2px_0_rgba(0,0,0,0.1)] transition-all hover:bg-accent-faint whitespace-nowrap"
|
||||
>
|
||||
Become a sponsor
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</a>
|
||||
<span className="text-xs text-muted-foreground/70">From $500 / month</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function SponsorStrip({ sponsors }: { sponsors: Sponsor[] }) {
|
||||
return (
|
||||
<section className="border-t border-border py-14">
|
||||
<div className="mx-auto max-w-6xl px-6">
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground/80">
|
||||
Sponsored by
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center justify-center gap-5 md:gap-6">
|
||||
{sponsors.map((sponsor) => (
|
||||
<a
|
||||
key={sponsor.name}
|
||||
href={sponsor.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={sponsor.name}
|
||||
className="group flex h-32 min-w-[260px] items-center justify-center rounded-2xl border border-border bg-card/40 backdrop-blur-sm px-10 transition-all hover:border-accent/40 hover:bg-card"
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={sponsor.logoSrc}
|
||||
alt={sponsor.logoAlt ?? sponsor.name}
|
||||
className={`h-14 w-auto max-w-[220px] object-contain opacity-80 transition-opacity group-hover:opacity-100 ${
|
||||
sponsor.invert ? 'brightness-0 invert' : ''
|
||||
}`}
|
||||
/>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 text-center">
|
||||
<a
|
||||
href="/sponsors"
|
||||
className="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground"
|
||||
>
|
||||
Become a sponsor
|
||||
<ArrowRight className="h-3.5 w-3.5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -5,8 +5,11 @@ export const LATEST_VERSION = 'v0.1.0';
|
||||
export const GITHUB_REPO = 'https://github.com/jamiepine/voicebox';
|
||||
export const GITHUB_RELEASES_PAGE = `${GITHUB_REPO}/releases`;
|
||||
export const DONATE_URL = 'https://buymeacoffee.com/jamiepine';
|
||||
export const SPONSOR_CHECKOUT_URL = 'https://buy.stripe.com/eVqdRad3n16ubcqf201Jm00';
|
||||
export const SPONSOR_CONTACT_EMAIL = '[email protected]';
|
||||
|
||||
// $VOICEBOX — the official community token on Solana
|
||||
export const TOKEN_TICKER = '$VOICEBOX';
|
||||
export const TOKEN_CONTRACT_ADDRESS = 'FpzZHtp5tbvz6xndEtoJHoGEWcT7cFEuscdCh9RApump';
|
||||
export const TOKEN_PUMP_URL = `https://pump.fun/coin/${TOKEN_CONTRACT_ADDRESS}`;
|
||||
|
||||
export const DOWNLOAD_LINKS = {
|
||||
macArm: GITHUB_RELEASES_PAGE,
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
export type Sponsor = {
|
||||
name: string;
|
||||
url: string;
|
||||
logoSrc: string;
|
||||
logoAlt?: string;
|
||||
tagline?: string;
|
||||
/** Set true for solid-black logos that need to render white on the dark theme. */
|
||||
invert?: boolean;
|
||||
};
|
||||
|
||||
export const SPONSORS: Sponsor[] = [
|
||||
];
|
||||
Reference in New Issue
Block a user