From 96e6bcdb8a6d20de1e2a42ff7e4eb8e2e11bbd2c Mon Sep 17 00:00:00 2001 From: Michel de Graaf Date: Sat, 25 Jul 2026 05:35:01 +0200 Subject: [PATCH] feat(website): route the OS links to per-platform install steps Clicking macOS, Linux or Windows on the home page landed on the file-path cards, which say where config lives, not how to install. Step 1 now carries one card per OS at those anchors: brew on macOS, cargo on Linux, cargo or the prebuilt .exe on Windows. The path cards keep their own -paths anchors. The hero logo drew into a fixed inline width, so the mobile height overrides shrank the mark inside a box sized for the desktop one. An aspect-ratio lets the width follow, and the mark is now larger and centred against the title. --- website/src/components/brand/logo.tsx | 2 +- website/src/components/ui/terminal.tsx | 9 +- website/src/pages/home.tsx | 10 +- website/src/pages/install.tsx | 126 +++++++++++++++++++------ 4 files changed, 112 insertions(+), 35 deletions(-) diff --git a/website/src/components/brand/logo.tsx b/website/src/components/brand/logo.tsx index 1f414b5..82fe510 100644 --- a/website/src/components/brand/logo.tsx +++ b/website/src/components/brand/logo.tsx @@ -12,6 +12,6 @@ export const Logo = ({ size = 180, className }: LogoProps) => ( role="img" aria-label="soulseek-rs" className={cn('logo-mark shrink-0 bg-contain bg-no-repeat', className)} - style={{ height: size, width: size * ASPECT }} + style={{ height: size, aspectRatio: ASPECT }} /> ) diff --git a/website/src/components/ui/terminal.tsx b/website/src/components/ui/terminal.tsx index e26220e..5d27549 100644 --- a/website/src/components/ui/terminal.tsx +++ b/website/src/components/ui/terminal.tsx @@ -20,9 +20,10 @@ interface TerminalProps { lines: readonly TermLine[] copy?: string className?: string + wrap?: boolean } -export const Terminal = ({ label, lines, copy, className }: TerminalProps) => { +export const Terminal = ({ label, lines, copy, className, wrap }: TerminalProps) => { const copyText = copy ?? lines @@ -47,7 +48,11 @@ export const Terminal = ({ label, lines, copy, className }: TerminalProps) => { {lines.map((line, i) => ( {line.t === 'cmd' && ( <> diff --git a/website/src/pages/home.tsx b/website/src/pages/home.tsx index 33ab9b2..47ff1aa 100644 --- a/website/src/pages/home.tsx +++ b/website/src/pages/home.tsx @@ -14,9 +14,9 @@ import { LINKS } from '@/lib/links' import { cn } from '@/lib/utils' const PLATFORMS: readonly { name: OsName; note: string; anchor: string }[] = [ - { name: 'macOS', note: 'Apple Keychain', anchor: 'macos' }, - { name: 'Linux', note: 'Secret Service', anchor: 'linux' }, - { name: 'Windows', note: 'Credential Manager', anchor: 'windows' }, + { name: 'macOS', note: 'brew or cargo', anchor: 'macos' }, + { name: 'Linux', note: 'cargo install', anchor: 'linux' }, + { name: 'Windows', note: 'cargo or .exe', anchor: 'windows' }, ] const FEATURES = [ @@ -124,8 +124,8 @@ export const Home = () => {
Soulseek client · terminal · Rust -
- +
+

A soulseek client for the terminal. Built for agents and people who live there. diff --git a/website/src/pages/install.tsx b/website/src/pages/install.tsx index c96b413..710389d 100644 --- a/website/src/pages/install.tsx +++ b/website/src/pages/install.tsx @@ -2,6 +2,7 @@ import { Button } from '@/components/ui/button' import { FeatureCard } from '@/components/ui/feature-card' import { Code } from '@/components/ui/inline-code' import { Cols, PageHead, Prose, Section, SectionHead } from '@/components/ui/layout' +import { OsIcon, type OsName } from '@/components/ui/os-icon' import { Callout } from '@/components/ui/panel' import { Terminal, type TermLine } from '@/components/ui/terminal' import { LINKS } from '@/lib/links' @@ -27,6 +28,76 @@ const LIB_LINES: TermLine[] = LIB_SRC.split('\n').map((text) => ({ text, })) +const ReleasesLink = ({ children }: { children: React.ReactNode }) => ( + + {children} + +) + +interface InstallRoute { + id: string + os: OsName + best: string + lines: readonly TermLine[] + alt: React.ReactNode +} + +const INSTALLS: readonly InstallRoute[] = [ + { + id: 'macos', + os: 'macOS', + best: 'Homebrew', + lines: [ + { t: 'cmd', text: 'brew install michel/tap/soulseek-rs' }, + { t: 'cmd', text: 'soulseek-rs' }, + ], + alt: ( + <> + A prebuilt binary for both Apple silicon and Intel, so no Rust toolchain. With one + installed, cargo install soulseek-rs builds it from crates.io instead. + + ), + }, + { + id: 'linux', + os: 'Linux', + best: 'cargo', + lines: [ + { t: 'cmd', text: 'cargo install soulseek-rs' }, + { t: 'cmd', text: 'soulseek-rs' }, + ], + alt: ( + <> + Homebrew on Linux works the same as on macOS. Without either, the{' '} + releases page has static musl archives for x86-64 and + arm64 that run on any distribution. + + ), + }, + { + id: 'windows', + os: 'Windows', + best: 'cargo or a prebuilt .exe', + lines: [ + { t: 'cmd', text: 'cargo install soulseek-rs' }, + { t: 'cmd', text: 'soulseek-rs.exe' }, + ], + alt: ( + <> + Cargo needs the MSVC build tools. To skip both, take the{' '} + pc-windows-msvc zip from the{' '} + releases page, unpack it, and put{' '} + soulseek-rs.exe on your PATH. + + ), + }, +] + interface PlatformNote { id: string name: string @@ -191,16 +262,7 @@ export const Install = () => {

- Homebrew, on macOS or Linux. A prebuilt binary, so no Rust toolchain: -

- -

- With a Rust toolchain from{' '} + One command per platform. Cargo needs a Rust toolchain from{' '} { > rustup.rs - , cargo works everywhere else: + ; the other routes don’t.

- +
+ {INSTALLS.map((route) => ( +
+
+ +

+ {route.os} +

+
+ + {route.best} + + +

+ {route.alt} +

+
+ ))} +

- Either way the binary lands on your PATH. Run soulseek-rs in a - terminal and the TUI opens, in a script or a pipe it wants a subcommand + Every route puts the same binary on your PATH. Run soulseek-rs in + a terminal and the TUI opens, in a script or a pipe it wants a subcommand instead.

-

- No Rust toolchain? Grab a prebuilt archive for your platform from the{' '} - - releases page - - , unpack it, and put the binary somewhere on your PATH. -

@@ -350,7 +422,7 @@ export const Install = () => { {PLATFORMS.map((platform) => (