diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8355ec..31b97f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,12 +2,6 @@ name: CI # Branch pushes are covered by the pull_request trigger. Listening to both for # every branch ran the whole matrix twice per PR. -# -# website/ holds the GitHub Pages site and no Rust. Ignoring it here must stay -# in lockstep with the same list in release.yml: the two skip together, so the -# release ci-gate never waits for a CI run that was never queued. Deliberately -# not applied to pull_request — CI is a required check there, and a skipped -# required check leaves the PR permanently unmergeable. on: push: branches: [master] diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index d4ff9bb..d18518b 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,7 +1,5 @@ name: Pages -# Only the website. The Rust workflows ignore this same path, so a site-only -# push runs this alone and a code-only push never rebuilds the site. on: push: branches: [master] @@ -29,8 +27,6 @@ jobs: with: bun-version: latest - # --frozen-lockfile so a drifted bun.lock fails here rather than - # silently shipping different dependency versions than were tested. - name: Install run: bun install --frozen-lockfile @@ -40,8 +36,6 @@ jobs: - name: Lint run: bun run lint - # Emits one static HTML file per route plus a 404, so direct links - # answer 200 with real markup instead of the SPA shell. - name: Build run: bun run build @@ -51,14 +45,10 @@ jobs: deploy: name: Deploy - # Pull requests get the build as a check; only master publishes. if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' needs: build runs-on: ubuntu-latest timeout-minutes: 10 - # Scoped to the deploy, not the workflow: a workflow-level group would - # queue PR builds behind an in-flight master deploy for no reason. Never - # cancel in flight — a half-uploaded artifact leaves the live site broken. concurrency: group: pages cancel-in-progress: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 707ec0b..e0ce3b9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,8 +2,6 @@ name: Release on: push: - # Must match ci.yml's paths-ignore exactly. A website-only push skips both - # workflows, so ci-gate is never left polling for an absent CI run. branches: [master] paths-ignore: ['website/**'] workflow_dispatch: @@ -51,10 +49,6 @@ jobs: 2>/dev/null || echo "unreachable") case "$verdict" in success) echo "CI passed for $GITHUB_SHA"; exit 0 ;; - # `cancelled` is what CI's own cancel-in-progress does to the - # older SHA when two pushes land back to back. Treating it as a - # failure turned every rapid push into a red release; keep - # waiting instead, and let the newer SHA's run decide. absent|running|unreachable|cancelled) ;; *) echo "::error::CI concluded '$verdict' for $GITHUB_SHA - refusing to release"; exit 1 ;; esac diff --git a/website/eslint.config.ts b/website/eslint.config.ts index 6b11a48..755a0fc 100644 --- a/website/eslint.config.ts +++ b/website/eslint.config.ts @@ -11,14 +11,9 @@ export default defineConfig( js.configs.recommended, - // strictTypeChecked over recommended: it needs type information (below) but - // catches the whole class of bugs that only types can see — floating - // promises, unsafe `any` flowing through, misused nullish operands. tseslint.configs.strictTypeChecked, tseslint.configs.stylisticTypeChecked, - // `configs.flat.*`, not `configs['recommended-latest']` — the latter is - // still the legacy eslintrc shape (plugins as a string array). reactHooks.configs.flat['recommended-latest'], jsxA11y.flatConfigs.recommended, @@ -41,8 +36,6 @@ export default defineConfig( { allowConstantExport: true }, ], - // Unused args are usually a mistake, but an `_`-prefixed one is an - // explicit "I know, I need the positional slot". '@typescript-eslint/no-unused-vars': [ 'error', { @@ -52,14 +45,11 @@ export default defineConfig( }, ], - // Type-only imports must be erasable, so the bundler can drop them - // without consulting the type graph. Pairs with verbatimModuleSyntax. '@typescript-eslint/consistent-type-imports': [ 'error', { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, ], - // A dropped promise in an event handler fails silently in production. '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-misused-promises': 'error', @@ -71,11 +61,6 @@ export default defineConfig( }, }, - // The TUI demo is a composite widget: it declares role="application" and - // owns its keys (j/k, arrows, 1-3) the way the real terminal client does. - // These two rules police exactly the pattern ARIA prescribes for that, so - // they are off for this one file rather than worked around in the markup. - // Every control inside it is still a native button or input. { files: ['src/components/tui/hero-terminal.tsx'], rules: { @@ -84,10 +69,6 @@ export default defineConfig( }, }, - // Config and build scripts are Node, not browser. Build output is meant to - // be read on the terminal, so console is the interface, not a stray debug - // statement. entry-server is a build entry point that never enters the HMR - // graph, so the component-only export rule does not apply to it. { files: ['*.config.ts', 'scripts/**/*.ts'], languageOptions: { diff --git a/website/index.html b/website/index.html index fca9f29..c713b41 100644 --- a/website/index.html +++ b/website/index.html @@ -3,8 +3,6 @@ - soulseek-rs, a Soulseek client for your terminal @@ -12,7 +10,6 @@ name="description" content="soulseek-rs is a keyboard-driven Soulseek client and a Rust protocol library. Search, share, browse, and chat from your terminal." /> - @@ -22,18 +19,10 @@ property="og:description" content="A Soulseek client and protocol library for Rust. Search, share, browse, and chat from your terminal." /> - - diff --git a/website/scripts/prerender.ts b/website/scripts/prerender.ts index f570267..a45ae65 100644 --- a/website/scripts/prerender.ts +++ b/website/scripts/prerender.ts @@ -1,23 +1,8 @@ -/* - * Turns the SPA build into real static files. - * - * Without this, GitHub Pages has no file at /install and answers a direct - * link with its 404 page. Writing one HTML file per route means every URL - * returns 200 with its own title, description, and fully rendered body; the - * client then hydrates it into the same interactive app. - * - * Run after both `vite build` and `vite build --ssr`. - */ import { mkdir, readFile, writeFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { NOT_FOUND_META, ROUTES, type RouteMeta } from '../src/lib/routes.ts' -/* - * Held in a variable so TypeScript does not try to resolve it: the module is - * a build artefact, and `bun run typecheck` runs before `vite build --ssr` - * has produced it. The cast is the contract with src/entry-server.tsx. - */ const SSR_ENTRY = '../dist-ssr/entry-server.js' const { render } = (await import(SSR_ENTRY)) as { render: (location: string) => string @@ -30,20 +15,12 @@ const SITE_URL = 'https://re-invention.nl/soulseek-rs/' const escapeAttr = (value: string): string => value.replace(/&/g, '&').replace(/"/g, '"').replace(/` matched by attribute and value. */ const setMeta = (html: string, attr: string, name: string, value: string): string => html.replace( new RegExp(`( { const canonical = meta.path === '/' ? SITE_URL : `${SITE_URL}${meta.path.slice(1)}/` let out = html.replace( @@ -84,8 +61,6 @@ for (const route of ROUTES) { await writePage(template, route, outPath, route.path) } -// GitHub Pages serves this whenever no file matches, which after the loop -// above is only ever a genuinely unknown path. await writePage(template, NOT_FOUND_META, '404.html', '/__not_found__') console.log(`done — ${String(ROUTES.length + 1)} pages`) diff --git a/website/src/App.tsx b/website/src/App.tsx index 45627ba..d8688a7 100644 --- a/website/src/App.tsx +++ b/website/src/App.tsx @@ -9,14 +9,6 @@ import { Home } from '@/pages/home' import { Install } from '@/pages/install' import { NotFound } from '@/pages/not-found' -/* - * Keyed by RoutePath, so adding an entry to ROUTES without a page here — or a - * page here that ROUTES does not know about — fails `bun run typecheck`. - * - * That drift used to be silent and shipped broken both ways: a prerendered - * file containing NotFound markup under the new page's title, or a real page - * with no prerendered file and "not found" in its tab. - */ const PAGES: Record = { '/': , '/install': , @@ -24,11 +16,6 @@ const PAGES: Record = { '/community': , } -/* - * Everything is eagerly imported: the whole site is a couple of hundred kB, - * so route-splitting would trade a network round-trip on every navigation for - * a bundle saving nobody would notice. - */ export const App = () => ( }> diff --git a/website/src/components/brand/logo.tsx b/website/src/components/brand/logo.tsx index ebfce85..1f414b5 100644 --- a/website/src/components/brand/logo.tsx +++ b/website/src/components/brand/logo.tsx @@ -1,23 +1,12 @@ import { cn } from '@/lib/utils' interface LogoProps { - /** Height in px. Width follows the artwork's aspect ratio. */ size?: number className?: string } -/** Native aspect ratio of the logo artwork (186.407 × 211). */ const ASPECT = 186.407 / 211 -/* - * Two assets rather than one recoloured file — the ink and light cuts differ - * in more than colour — swapped by CSS. - * - * Painted as CSS background images rather than image elements: a hidden one - * is still downloaded, so the two-element version fetched both cuts on every - * visit (~12-23 kB gzip wasted above the fold). A background on a rule that - * never paints is never requested. - */ export const Logo = ({ size = 180, className }: LogoProps) => (
( export const Footer = () => (