Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/src/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
import Link from "next/link";
import { ArrowRight, ArrowUpRight } from "lucide-react";
import { ArrowRight, ArrowUpRight, Info } from "lucide-react";
import SectionIntro from "@/components/sections/SectionIntro";
import shell from "@/components/sections/SectionShell.module.css";
import { BRAND, BRAND_DOMAIN, BRAND_GITHUB, BRAND_X, LEGACY_DOMAIN } from "@/lib/brand";
Expand Down Expand Up @@ -34,6 +34,7 @@ export default function AboutPage() {
<main className={shell.page}>
<SectionIntro
eyebrow="About"
icon={<Info />}
title={`What ${BRAND} is`}
description={<>{BRAND} is a free, open-source token launchpad. One transaction deploys a fixed-supply ERC-20, opens a Uniswap v4 pool at the market cap you pick and locks 100% of the supply as liquidity, forever. It runs on Base, Robinhood Chain and Arc.</>}
>
Expand Down
24 changes: 13 additions & 11 deletions app/src/app/agents/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
import { ArrowDown, ArrowRight, ArrowUpRight, Braces, FileCode2, Terminal } from "lucide-react";
import { ArrowDown, ArrowLeftRight, ArrowRight, ArrowUpRight, BookOpen, Bot, Braces, FileCode2, Rocket, Terminal } from "lucide-react";
import SectionIntro from "@/components/sections/SectionIntro";
import AgentsCodeBlock from "@/components/sections/AgentsCodeBlock";
import shell from "@/components/sections/SectionShell.module.css";
import styles from "@/components/sections/Agents.module.css";
import { launchpad } from "@/lib/launchpad/config";
import { CHAIN_KEYS, CHAIN_LABELS, CHAINS, SITE_URL, explorerAddress } from "@/lib/chainPublic";
import { BRAND_GITHUB } from "@/lib/brand";
import { ChainLogo } from "@/components/launchpad/ChainLogo";

export const metadata: Metadata = pageMetadata({ path: "/agents", title: "Agents", description: "Launch and trade tokens from an agent: one contract call, plus a JSON API for the list, trades and metadata." });

/** The page's sections, each with the mark its heading carries. */
const sections = [
{ id: "launch", number: "01", label: "Launch a token" },
{ id: "read", number: "02", label: "Read the API" },
{ id: "trade", number: "03", label: "Trade & collect" },
{ id: "launch", icon: Rocket, label: "Launch a token" },
{ id: "read", icon: BookOpen, label: "Read the API" },
{ id: "trade", icon: ArrowLeftRight, label: "Trade & collect" },
] as const;

const endpoints = [
Expand All @@ -34,33 +36,33 @@ export default function AgentsPage() {

return (
<main className={`${shell.page} ${styles.page}`}>
<SectionIntro eyebrow="Developer reference" title="Agents" description={<>No API key, no signup, no platform fee. A launch is one contract call from any wallet with a little ETH for gas. Everything the site shows is also JSON.</>}>
<SectionIntro eyebrow="Developer reference" icon={<Bot />} title="Agents" description={<>No API key, no signup, no platform fee. A launch is one contract call from any wallet with a little ETH for gas. Everything the site shows is also JSON.</>}>
<a href="/llms.txt" className={shell.action}><FileCode2 size={16} aria-hidden="true" />Read llms.txt<ArrowUpRight size={14} aria-hidden="true" /></a>
<a href={BRAND_GITHUB} target="_blank" rel="noreferrer" className={shell.textLink}>Explore the source<ArrowUpRight size={14} aria-hidden="true" /></a>
</SectionIntro>

<div className={styles.entry}>
<div className={styles.entryHeading}><Terminal size={19} aria-hidden="true" /><p>The interface is optional.<span>The same protocol is yours to build with.</span></p></div>
<div className={styles.entryHeading}><span className={styles.entryIcon} aria-hidden="true"><Terminal size={17} /></span><p>The interface is optional.<span>The same protocol is yours to build with.</span></p></div>
<a href="#launch">Start with a launch<ArrowDown size={15} aria-hidden="true" /></a>
</div>

<div className={styles.layout}>
<aside className={styles.sidebar}>
<nav aria-label="Agent documentation sections">
<p className={styles.indexLabel}>On this page</p>
{sections.map(({ id, number, label }) => <a key={id} href={`#${id}`}><span>{number}</span>{label}<ArrowRight size={13} aria-hidden="true" /></a>)}
{sections.map(({ id, icon: Icon, label }) => <a key={id} href={`#${id}`}><span aria-hidden="true"><Icon size={13} /></span>{label}<ArrowRight size={13} aria-hidden="true" /></a>)}
</nav>
<p className={styles.sidebarNote}>Use the JSON API to read.<br />Use your wallet to write on-chain.</p>
</aside>

<div className={styles.content}>
<section id="launch" tabIndex={-1} className={`${shell.anchorSection} ${styles.section}`} aria-labelledby="agents-launch-title">
<div className={styles.sectionHeading}><span className={styles.sectionNumber}>01</span><div><h2 id="agents-launch-title">Launch a token</h2><p>One call, from your own wallet.</p></div></div>
<div className={styles.sectionHeading}><span className={styles.sectionIcon} aria-hidden="true"><Rocket size={17} /></span><div><h2 id="agents-launch-title">Launch a token</h2><p>One call, from your own wallet.</p></div></div>
<div className={styles.networks}>
{CHAIN_KEYS.map((chain) => {
const config = launchpad(chain);
return <div key={chain} className={styles.network}>
<div className={styles.networkHeading}><h3>{CHAIN_LABELS[chain]}</h3><span>Chain <b>{CHAINS[chain].id}</b></span></div>
<div className={styles.networkHeading}><h3><ChainLogo chain={chain} size={16} />{CHAIN_LABELS[chain]}</h3><span>Chain <b>{CHAINS[chain].id}</b></span></div>
<dl>{(["factory", "locker"] as const).map((contract) => <div key={contract}>
<dt>{contract}</dt>
<dd>{config[contract] ? <a href={explorerAddress(chain, config[contract])} target="_blank" rel="noreferrer" aria-label={`${CHAIN_LABELS[chain]} ${contract}: ${config[contract]}, view on explorer`}><code>{config[contract]}</code><ArrowUpRight size={13} aria-hidden="true" /></a> : <span>not deployed yet</span>}</dd>
Expand Down Expand Up @@ -90,7 +92,7 @@ export default function AgentsPage() {
</section>

<section id="read" tabIndex={-1} className={`${shell.anchorSection} ${styles.section}`} aria-labelledby="agents-read-title">
<div className={styles.sectionHeading}><span className={styles.sectionNumber}>02</span><div><h2 id="agents-read-title">Read the API</h2><p>JSON endpoints, plus a plain-text agent reference.</p></div></div>
<div className={styles.sectionHeading}><span className={styles.sectionIcon} aria-hidden="true"><BookOpen size={17} /></span><div><h2 id="agents-read-title">Read the API</h2><p>JSON endpoints, plus a plain-text agent reference.</p></div></div>
<div className={styles.endpoints}>
<div className={styles.endpointHeader}><span>Public endpoints</span><span>No API key</span></div>
{endpoints.map((endpoint) => <div key={endpoint.path} className={styles.endpoint}>
Expand All @@ -106,7 +108,7 @@ export default function AgentsPage() {
</section>

<section id="trade" tabIndex={-1} className={`${shell.anchorSection} ${styles.section}`} aria-labelledby="agents-trade-title">
<div className={styles.sectionHeading}><span className={styles.sectionNumber}>03</span><div><h2 id="agents-trade-title">Trade & collect</h2><p>Standard pools. Direct contract calls.</p></div></div>
<div className={styles.sectionHeading}><span className={styles.sectionIcon} aria-hidden="true"><ArrowLeftRight size={17} /></span><div><h2 id="agents-trade-title">Trade & collect</h2><p>Standard pools. Direct contract calls.</p></div></div>
<div className={styles.tradeReference}>
<div><span className={styles.tradeLabel}>Swap</span><h3>Speak Uniswap v4</h3><p className={styles.prose}>Pools are plain Uniswap v4, with tick spacing <code>200</code> and no hook. The quote can be ETH, TWIG (Base), GITLAWB (Base and Robinhood Chain), USDG (Robinhood Chain), USDC (Arc) or a supported issuer-registry stock token. Get the actual pool key with <code>poolKeyOf(token)</code> on the factory. Swap through the Universal Router with a <code>V4_SWAP</code> command, or any router that speaks v4.</p></div>
<div><span className={styles.tradeLabel}>Collect</span><h3>Pay out accrued fees</h3><p className={styles.prose}>Anyone may call <code>collect(tokenId)</code> on that chain&apos;s locker to pay out accrued fees. Base locker: <code>{locker}</code>. Use the matching chain address above.</p></div>
Expand Down
17 changes: 8 additions & 9 deletions app/src/app/feed/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function Loading() {
return (
<main className={shell.page} aria-busy="true" aria-label="Loading posts">
<header className={shell.intro}>
<div className="flex h-4 items-center"><Sk className="h-3 w-36" /></div>
<Sk className="h-7 w-40 rounded-full" />
<div className={shell.introRow}>
<div className={`${shell.introCopy} w-full`}>
<Sk className="h-9 w-72 max-w-full sm:h-[50px]" />
Expand All @@ -19,13 +19,13 @@ export default function Loading() {
<div className="flex h-[26px] items-center sm:h-[27px]"><Sk className="h-4 w-3/4 max-w-[460px]" /></div>
</div>
</div>
<Sk className="h-11 w-40 rounded-full" />
<Sk className="h-11 w-40 rounded-xl" />
</div>
</header>
<div className={styles.layout}>
<section className={shell.panel} aria-label="Loading recent community posts">
<div className={styles.toolbar}><Sk className="h-4 w-44 max-w-full" /><Sk className="h-11 w-11 rounded-full" /></div>
<div className={styles.filters}><Sk className="h-[54px] w-60 max-w-full rounded-xl" /><Sk className="h-11 min-w-[180px] flex-1" /></div>
<div className={styles.filters}><Sk className="h-[54px] w-60 max-w-full rounded-xl" /><Sk className="h-11 min-w-[180px] flex-1 rounded-xl" /></div>
<div className={styles.resultLine}><p className="flex h-4 items-center"><Sk className="h-3 w-24" /></p><span className="flex h-4 items-center"><Sk className="h-3 w-36" /></span></div>
<ul>
{Array.from({ length: 6 }, (_, i) => (
Expand All @@ -36,22 +36,21 @@ export default function Loading() {
</section>
<aside className={styles.aside} aria-hidden="true">
<section className={styles.guide}>
<Sk className="h-3 w-44" />
<div className="mt-3.5 flex h-[30px] items-center"><Sk className="h-6 w-48 max-w-full" /></div>
<div className="mt-3">
<div className="flex h-[23px] items-center"><Sk className="h-5 w-48 max-w-full" /></div>
<div className="mt-2">
<div className="flex h-[23px] items-center"><Sk className="h-3.5 w-full" /></div>
<div className="flex h-[23px] items-center"><Sk className="h-3.5 w-5/6" /></div>
</div>
<ul className={styles.steps}>
{[0, 1, 2].map((i) => (
<li key={i}><span className="flex h-5 shrink-0 items-center"><Sk className="h-3 w-5" /></span><div className="min-w-0 flex-1"><div className="flex h-5 items-center"><Sk className="h-3.5 w-32 max-w-full" /></div><div className="mt-1 flex h-5 items-center"><Sk className="h-3 w-44 max-w-full" /></div></div></li>
<li key={i}><Sk className="size-8 shrink-0 rounded-[10px]" /><div className="min-w-0 flex-1"><div className="flex h-5 items-center"><Sk className="h-3.5 w-32 max-w-full" /></div><div className="mt-1 flex h-5 items-center"><Sk className="h-3 w-44 max-w-full" /></div></div></li>
))}
</ul>
<div className="flex h-11 items-center"><Sk className="h-3.5 w-32" /></div>
</section>
<section className={styles.note}>
<Sk className="h-5 w-5" />
<div className="mt-4 flex h-[21px] items-center"><Sk className="h-4 w-44 max-w-full" /></div>
<Sk className="size-8 rounded-[10px]" />
<div className="mt-3.5 flex h-[21px] items-center"><Sk className="h-4 w-44 max-w-full" /></div>
<div className="mt-2.5">
{["w-full", "w-full", "w-full", "w-2/3"].map((w, i) => <div key={i} className="flex h-[21px] items-center"><Sk className={`h-3 ${w}`} /></div>)}
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/src/app/feed/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
import Link from "next/link";
import { ArrowUpRight } from "lucide-react";
import { ArrowUpRight, MessagesSquare } from "lucide-react";
import CommunityFeed from "@/components/sections/CommunityFeed";
import SectionIntro from "@/components/sections/SectionIntro";
import styles from "@/components/sections/SectionShell.module.css";
Expand All @@ -14,7 +14,7 @@ export default async function FeedPage() {
const result = await listFeed(100).then((posts) => ({ posts, failed: false })).catch(() => ({ posts: [], failed: true }));
return (
<main className={styles.page}>
<SectionIntro eyebrow="Community / Posts" title="Behind every token." description="Holders, traders and creators, in their own words. Follow the conversation across Base, Robinhood Chain and Arc.">
<SectionIntro eyebrow="Community posts" icon={<MessagesSquare />} title="Behind every token." description="Holders, traders and creators, in their own words. Follow the conversation across Base, Robinhood Chain and Arc.">
<Link href="/#launches" className={styles.action}>Explore tokens <ArrowUpRight size={16} aria-hidden="true" /></Link>
</SectionIntro>
<CommunityFeed initial={result.posts} loadError={result.failed} />
Expand Down
24 changes: 18 additions & 6 deletions app/src/app/me/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
import { Sk, SkRow, SkStat } from "@/components/Skeleton";
import shell from "@/components/sections/SectionShell.module.css";

/* The dashboard page's own frame (its SectionIntro header), then the wallet card, the figures and the rows, so nothing
shifts when the dashboard arrives. */
export default function Loading() {
return (
<main className="mx-auto max-w-4xl px-4 pt-8 sm:pt-10 pb-16 space-y-6" aria-busy="true" aria-label="loading dashboard">
<header className="space-y-3">
<Sk className="h-9 w-20" />
<Sk className="h-5 w-3/4 max-w-xl" />
<main className={shell.page} aria-busy="true" aria-label="loading dashboard">
<header className={shell.intro}>
<Sk className="h-7 w-36 rounded-full" />
<div className={shell.introRow}>
<div className={`${shell.introCopy} w-full`}>
<Sk className="h-9 w-80 max-w-full sm:h-[50px]" />
<div className="mt-3.5 space-y-2.5">
<Sk className="h-4 w-full max-w-[620px]" />
<Sk className="h-4 w-2/3 max-w-[420px]" />
</div>
</div>
</div>
</header>
<dl className="grid grid-cols-2 sm:grid-cols-4 gap-2.5">
<Sk className="h-16 w-full rounded-2xl" />
<dl className="mt-4 grid grid-cols-2 gap-2 sm:grid-cols-4">
{Array.from({ length: 4 }, (_, i) => (
<SkStat key={i} />
))}
</dl>
<ul className="rounded-2xl bg-card border border-line shadow-card overflow-hidden">
<ul className="mt-5 overflow-hidden rounded-2xl border border-line bg-card">
{Array.from({ length: 4 }, (_, i) => (
<SkRow key={i} i={i} />
))}
Expand Down
3 changes: 2 additions & 1 deletion app/src/app/me/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Metadata } from "next";
import MeDashboard from "@/components/launchpad/MeDashboard";
import { LayoutDashboard } from "lucide-react";
import SectionIntro from "@/components/sections/SectionIntro";
import shell from "@/components/sections/SectionShell.module.css";

Expand All @@ -10,7 +11,7 @@ export const dynamic = "force-dynamic";
export default function MePage() {
return (
<main className={shell.page}>
<SectionIntro eyebrow="Your dashboard" title="A space for your next move." description="Your launches, the fees they earn, and everything you trade. One wallet across Base, Robinhood Chain and Arc." />
<SectionIntro eyebrow="Your dashboard" icon={<LayoutDashboard />} title="A space for your next move." description="Your launches, the fees they earn, and everything you trade. One wallet across Base, Robinhood Chain and Arc." />
<MeDashboard />
</main>
);
Expand Down
Loading
Loading