Two TUIs on one daemon behaved as two separate windows that happened to share a login: each showed only the transfers it had queued and the searches it had run, and neither said it was attached to anything. The status bar now names the daemon, and both lists are views of the session: transfers and searches appear in every window, dismissing one dismisses it everywhere, and a window opened later starts from what is already running rather than blank. message read likewise starts from the conversation the daemon collected instead of only what arrives while it runs. Search state needed the daemon to say when a query went out. The window a search collects for belongs to whichever client started it, so without that every other window showed a running search as finished the moment it appeared — which is what it did. The summary carries a file count too, so a window has the right number before it has pulled the results across. Adds Client::forget_search, which the library had no way to do: a resident client otherwise holds every result set it has ever seen, and a window that dismissed a search had no way to say so. A review of the first cut found five defects, all fixed here and all covered: syncing ran locally too, where it cost a full copy of every result set per frame and deleted the completed-download history restored from disk; a transfer could be listed twice by racing its own registration; clearing all searches did not reach the session, so every row came back; a search dismissed elsewhere never left; and a wishlist search reported as finished the instant it went out.
website
The soulseek-rs project site. Vite + React + TypeScript + Tailwind v4,
deployed to GitHub Pages by .github/workflows/pages.yml.
bun install
bun run dev # http://localhost:5173/soulseek-rs/
bun run build # client build, SSR build, then prerender
bun run typecheck
bun run lint
How the static site is produced
bun run build runs three steps:
vite build— the client bundle, plusdist/index.htmlas the template.vite build --ssr src/entry-server.tsx— the same app compiled for Node.bun scripts/prerender.ts— renders every route insrc/lib/routes.tsto its own file (dist/install/index.html, and so on) with that route's title and description substituted in, plus a404.html.
Without step 3 GitHub Pages has no file at /install and answers a direct link
with a 404. With it, every URL returns 200 with fully rendered markup, and the
client hydrates that markup rather than replacing it.
Adding a page means: a component in src/pages/, a <Route> in src/App.tsx,
and an entry in src/lib/routes.ts. The last one is what the prerenderer walks.
Design system
Tokens live in src/index.css, ported from the Claude Design project's
tokens/colors.css. They are the same values that back the TUI in
soulseek-rs/src/ui/styles.rs — change one, change both.
Dark is the native theme; light re-cuts the semantic hues roughly 10% darker,
because phosphor and tape are legible on vinyl but wash out on manila. Theme
selection is [data-theme] on <html>, defaulting to the OS setting, with an
inline script in index.html applying it before first paint so the page never
flashes the wrong background.
Notes
baseinvite.config.tsis/soulseek-rs/because this is a project site. Pointing a custom domain at it means setting that to/(the router readsimport.meta.env.BASE_URL, so it follows automatically).- Copy in
src/pages/follows the project's tone of voice: plainspoken, precise, honest about limits first. No copyrighted artist or album names anywhere, including the TUI demo data insrc/components/tui/data.ts.