refactor(website): split the hero terminal and the page components

HeroTerminal was a single 367-line component doing three separate jobs:
simulating the transfer queue, holding the per-search result sets, and
rendering. It is now useTransfers, useResults and useHeroDemo behind a
presentational component. The dead shellRef went with it.

The home, docs and install pages each returned one long JSX block; every
section is now a named component, so the page body reads as an outline.

The prerendered HTML is byte-identical for all five pages, and the extracted
transfer reducer matches the original over 200 ticks.
This commit is contained in:
Michel de Graaf
2026-07-26 15:40:58 +02:00
parent 093f32b360
commit 2e9cd6b860
7 changed files with 1128 additions and 1028 deletions
+205 -197
View File
@@ -117,202 +117,210 @@ const FEATURES = [
},
] as const
export const Home = () => {
return (
<>
<section className="pt-5 pb-8 sm:pt-7 sm:pb-10">
<Wrap>
<div className="flex flex-col gap-[22px]">
<Eyebrow className="max-md:hidden">Soulseek client · terminal · Rust</Eyebrow>
<div className="flex items-center gap-3.5 sm:gap-6 md:items-start md:gap-11">
<Logo size={172} className="max-md:!h-32 md:mt-2" />
<h1 className="text-[29px] leading-[37px] font-bold tracking-[-0.01em] text-balance sm:text-[34px] sm:leading-[42px] md:text-display md:leading-[var(--text-display--line-height)]">
A soulseek client for the terminal. Built for agents and people who live
there.
</h1>
</div>
<p className="text-lg leading-[29px] text-pretty text-secondary max-md:hidden">
Search the network, share your files, browse someone&rsquo;s collection, join
a room. It runs over ssh on the machine where your music already lives.
</p>
<div className="flex flex-wrap items-center justify-between gap-x-5 gap-y-3 sm:gap-y-4">
<div className="flex flex-wrap items-center gap-2 max-md:w-full max-md:justify-center sm:gap-3">
<Button href="/install" variant="accent">
Install
</Button>
<Button href="/docs">Read the docs</Button>
<Button href={LINKS.gh}>GitHub</Button>
</div>
<CommandPill text="brew install michel/tap/soulseek-rs" />
</div>
</div>
<div className="mt-3.5 overflow-hidden">
<HeroTerminal />
</div>
</Wrap>
</section>
<section className="border-t border-hairline py-6">
<Wrap>
<div className="flex flex-wrap items-center gap-3.5 sm:gap-8">
<Eyebrow className="max-[519px]:hidden">Runs on</Eyebrow>
<ul className="flex w-full flex-nowrap justify-center min-[520px]:flex-wrap min-[520px]:justify-start sm:w-auto">
{PLATFORMS.map((platform, i) => (
<li
key={platform.name}
className={cn(
'flex w-auto border-hairline px-[18px] first:border-l-0 first:pl-0 not-first:border-l',
'min-[520px]:w-full min-[520px]:border-l-0 min-[520px]:px-0',
i === 0
? 'sm:w-auto sm:pr-6.5'
: 'sm:w-auto sm:border-l sm:px-6.5',
)}
>
<Link
to={`/install#${platform.anchor}`}
className="group flex min-h-11 w-auto items-center justify-center gap-2.5 border-hairline py-1.5 min-[520px]:w-full min-[520px]:justify-start min-[520px]:border-t min-[520px]:py-2.5 min-[520px]:first:border-t-0 sm:w-auto sm:border-t-0 sm:py-0"
>
<OsIcon name={platform.name} className="max-[519px]:size-[26px]" />
<span className="flex flex-col leading-[1.35] max-[519px]:hidden">
<b className="text-[13.5px] font-medium text-primary transition-colors group-hover:text-accent-text">
{platform.name}
</b>
<span className="text-[11.5px] text-secondary">{platform.note}</span>
</span>
</Link>
</li>
))}
</ul>
</div>
</Wrap>
</section>
<Section id="about">
<SectionHead
eyebrow="what it is"
title="A client for a network that runs on people."
>
No charts, no recommendations. You search, you find the person who has it, you
download it from them.
</SectionHead>
<Cols>
<Prose>
<p>
Soulseek is a peer-to-peer network from the early 2000s. People still use it
to share niche music by hand: the album that exists in one person&rsquo;s
folder and nowhere else.
</p>
<p>
soulseek-rs speaks that protocol. It&rsquo;s a third-party client, not the
official app. It shares, browses, and does rooms and private messages,
because a client that only downloads takes without giving anything back.
</p>
</Prose>
<div className="flex flex-col gap-4">
<Callout title="build your own">
<p>
Reach for <Code>soulseek-rs-lib</Code> to build a custom client or an agent
on the protocol.{' '}
<Link to="/install" className="text-link hover:text-link-hover">
Use the library
</Link>
</p>
</Callout>
<Callout title="agents and automation">
<p>
Every command runs as a one-off from the shell. Point cron, a script, or an
agent at them and read JSON records back off stdout. No daemon, no API to
stand up. <Code>skills install</Code> teaches the agent the surface for
you.{' '}
<Link to="/docs" className="text-link hover:text-link-hover">
See the commands
</Link>
</p>
</Callout>
</div>
</Cols>
</Section>
<Section id="features">
<SectionHead eyebrow="features" title="What it does.">
Everything happens in the TUI, and every command also runs as a one-off from the
shell.
</SectionHead>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
{FEATURES.map((feature, i) => (
<FeatureCard
key={feature.title}
glyph={feature.glyph}
color={feature.color}
title={feature.title}
cmd={feature.cmd}
className={i === 0 ? 'lg:col-span-2' : ''}
>
{feature.body}
</FeatureCard>
))}
const Hero = () => (
<section className="pt-5 pb-8 sm:pt-7 sm:pb-10">
<Wrap>
<div className="flex flex-col gap-[22px]">
<Eyebrow className="max-md:hidden">Soulseek client · terminal · Rust</Eyebrow>
<div className="flex items-center gap-3.5 sm:gap-6 md:items-start md:gap-11">
<Logo size={172} className="max-md:!h-32 md:mt-2" />
<h1 className="text-[29px] leading-[37px] font-bold tracking-[-0.01em] text-balance sm:text-[34px] sm:leading-[42px] md:text-display md:leading-[var(--text-display--line-height)]">
A soulseek client for the terminal. Built for agents and people who live there.
</h1>
</div>
</Section>
<Section id="agents">
<SectionHead eyebrow="agent native" title="Ask for the record. Skip the pipeline.">
An agent can run any CLI. <Code>skills install</Code> teaches it to run this one
well.
</SectionHead>
<img
src={`${import.meta.env.BASE_URL}agent-demo.svg`}
alt="an agent loading the soulseek-rs skill and downloading tracks"
className="w-full"
/>
<Prose className="mt-11 max-w-[660px]">
<p>
<Code>--help</Code> lists flags. It cannot say which JSON keys a record
carries, or that exit 4 means widen the query rather than retry it. An agent
without that guesses, and guessing against a stranger&rsquo;s file server
hangs the job.
</p>
<p>
<Code>skills install</Code> fills that in. The requests stop being pipelines
you write: mirror a peer&rsquo;s FLAC folder overnight, or check whether a
user is worth queueing behind before a 2 GB transfer.
</p>
<p>
It stays a CLI: no daemon, no MCP server, no API key. Upgrading the binary
upgrades the skill; rerun <Code>skills install</Code> to take it.{' '}
<Link to="/docs" className="text-link hover:text-link-hover">
See the commands
</Link>
</p>
</Prose>
</Section>
<Section band>
<Cols center>
<div className="flex flex-col gap-3.5">
<Eyebrow>get started</Eyebrow>
<h2 className="text-[26px] leading-[34px] font-medium text-balance sm:text-title sm:leading-[var(--text-title--line-height)]">
Install it, then press <span className="text-accent-text">s</span> to search.
</h2>
<p className="text-secondary">
One command on macOS or Linux, then the TUI opens. No Rust toolchain needed.
</p>
<div className="mt-1.5 flex flex-wrap gap-2 sm:gap-3">
<Button href="/docs" variant="accent">
Read the docs
</Button>
<Button href="/install">Install options</Button>
</div>
<p className="text-lg leading-[29px] text-pretty text-secondary max-md:hidden">
Search the network, share your files, browse someone&rsquo;s collection, join a
room. It runs over ssh on the machine where your music already lives.
</p>
<div className="flex flex-wrap items-center justify-between gap-x-5 gap-y-3 sm:gap-y-4">
<div className="flex flex-wrap items-center gap-2 max-md:w-full max-md:justify-center sm:gap-3">
<Button href="/install" variant="accent">
Install
</Button>
<Button href="/docs">Read the docs</Button>
<Button href={LINKS.gh}>GitHub</Button>
</div>
<Terminal
label="quick start"
lines={[
{ t: 'cmd', text: 'brew install michel/tap/soulseek-rs' },
{ t: 'cmd', text: 'soulseek-rs' },
]}
/>
</Cols>
</Section>
</>
)
}
<CommandPill text="brew install michel/tap/soulseek-rs" />
</div>
</div>
<div className="mt-3.5 overflow-hidden">
<HeroTerminal />
</div>
</Wrap>
</section>
)
const RunsOn = () => (
<section className="border-t border-hairline py-6">
<Wrap>
<div className="flex flex-wrap items-center gap-3.5 sm:gap-8">
<Eyebrow className="max-[519px]:hidden">Runs on</Eyebrow>
<ul className="flex w-full flex-nowrap justify-center min-[520px]:flex-wrap min-[520px]:justify-start sm:w-auto">
{PLATFORMS.map((platform, i) => (
<li
key={platform.name}
className={cn(
'flex w-auto border-hairline px-[18px] first:border-l-0 first:pl-0 not-first:border-l',
'min-[520px]:w-full min-[520px]:border-l-0 min-[520px]:px-0',
i === 0 ? 'sm:w-auto sm:pr-6.5' : 'sm:w-auto sm:border-l sm:px-6.5',
)}
>
<Link
to={`/install#${platform.anchor}`}
className="group flex min-h-11 w-auto items-center justify-center gap-2.5 border-hairline py-1.5 min-[520px]:w-full min-[520px]:justify-start min-[520px]:border-t min-[520px]:py-2.5 min-[520px]:first:border-t-0 sm:w-auto sm:border-t-0 sm:py-0"
>
<OsIcon name={platform.name} className="max-[519px]:size-[26px]" />
<span className="flex flex-col leading-[1.35] max-[519px]:hidden">
<b className="text-[13.5px] font-medium text-primary transition-colors group-hover:text-accent-text">
{platform.name}
</b>
<span className="text-[11.5px] text-secondary">{platform.note}</span>
</span>
</Link>
</li>
))}
</ul>
</div>
</Wrap>
</section>
)
const About = () => (
<Section id="about">
<SectionHead eyebrow="what it is" title="A client for a network that runs on people.">
No charts, no recommendations. You search, you find the person who has it, you
download it from them.
</SectionHead>
<Cols>
<Prose>
<p>
Soulseek is a peer-to-peer network from the early 2000s. People still use it to
share niche music by hand: the album that exists in one person&rsquo;s folder and
nowhere else.
</p>
<p>
soulseek-rs speaks that protocol. It&rsquo;s a third-party client, not the
official app. It shares, browses, and does rooms and private messages, because a
client that only downloads takes without giving anything back.
</p>
</Prose>
<div className="flex flex-col gap-4">
<Callout title="build your own">
<p>
Reach for <Code>soulseek-rs-lib</Code> to build a custom client or an agent on
the protocol.{' '}
<Link to="/install" className="text-link hover:text-link-hover">
Use the library
</Link>
</p>
</Callout>
<Callout title="agents and automation">
<p>
Every command runs as a one-off from the shell. Point cron, a script, or an
agent at them and read JSON records back off stdout. No daemon, no API to stand
up. <Code>skills install</Code> teaches the agent the surface for you.{' '}
<Link to="/docs" className="text-link hover:text-link-hover">
See the commands
</Link>
</p>
</Callout>
</div>
</Cols>
</Section>
)
const Features = () => (
<Section id="features">
<SectionHead eyebrow="features" title="What it does.">
Everything happens in the TUI, and every command also runs as a one-off from the
shell.
</SectionHead>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
{FEATURES.map((feature, i) => (
<FeatureCard
key={feature.title}
glyph={feature.glyph}
color={feature.color}
title={feature.title}
cmd={feature.cmd}
className={i === 0 ? 'lg:col-span-2' : ''}
>
{feature.body}
</FeatureCard>
))}
</div>
</Section>
)
const Agents = () => (
<Section id="agents">
<SectionHead eyebrow="agent native" title="Ask for the record. Skip the pipeline.">
An agent can run any CLI. <Code>skills install</Code> teaches it to run this one well.
</SectionHead>
<img
src={`${import.meta.env.BASE_URL}agent-demo.svg`}
alt="an agent loading the soulseek-rs skill and downloading tracks"
className="w-full"
/>
<Prose className="mt-11 max-w-[660px]">
<p>
<Code>--help</Code> lists flags. It cannot say which JSON keys a record carries, or
that exit 4 means widen the query rather than retry it. An agent without that
guesses, and guessing against a stranger&rsquo;s file server hangs the job.
</p>
<p>
<Code>skills install</Code> fills that in. The requests stop being pipelines you
write: mirror a peer&rsquo;s FLAC folder overnight, or check whether a user is worth
queueing behind before a 2 GB transfer.
</p>
<p>
It stays a CLI: no daemon, no MCP server, no API key. Upgrading the binary upgrades
the skill; rerun <Code>skills install</Code> to take it.{' '}
<Link to="/docs" className="text-link hover:text-link-hover">
See the commands
</Link>
</p>
</Prose>
</Section>
)
const GetStarted = () => (
<Section band>
<Cols center>
<div className="flex flex-col gap-3.5">
<Eyebrow>get started</Eyebrow>
<h2 className="text-[26px] leading-[34px] font-medium text-balance sm:text-title sm:leading-[var(--text-title--line-height)]">
Install it, then press <span className="text-accent-text">s</span> to search.
</h2>
<p className="text-secondary">
One command on macOS or Linux, then the TUI opens. No Rust toolchain needed.
</p>
<div className="mt-1.5 flex flex-wrap gap-2 sm:gap-3">
<Button href="/docs" variant="accent">
Read the docs
</Button>
<Button href="/install">Install options</Button>
</div>
</div>
<Terminal
label="quick start"
lines={[
{ t: 'cmd', text: 'brew install michel/tap/soulseek-rs' },
{ t: 'cmd', text: 'soulseek-rs' },
]}
/>
</Cols>
</Section>
)
export const Home = () => (
<>
<Hero />
<RunsOn />
<About />
<Features />
<Agents />
<GetStarted />
</>
)