mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-19 06:40:38 -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:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user