From 503a0a7bc853e364f2a40d2a4cb898e988913e1b Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Mon, 22 Dec 2025 16:12:54 +0100 Subject: [PATCH 1/4] svelte: Add `@crates-io/api-client` dependency --- pnpm-lock.yaml | 4 ++++ svelte/package.json | 3 +++ 2 files changed, 7 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76811c8ecb..220bf61a88 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -364,6 +364,10 @@ importers: version: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@25.0.1)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(terser@5.44.1)(yaml@2.8.2) svelte: + dependencies: + '@crates-io/api-client': + specifier: workspace:* + version: link:../packages/crates-io-api-client devDependencies: '@chromatic-com/storybook': specifier: 4.1.3 diff --git a/svelte/package.json b/svelte/package.json index d82ad4e24e..a21d4acb2c 100644 --- a/svelte/package.json +++ b/svelte/package.json @@ -21,6 +21,9 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, + "dependencies": { + "@crates-io/api-client": "workspace:*" + }, "devDependencies": { "@chromatic-com/storybook": "4.1.3", "@eslint/compat": "2.0.0", From adf22bee5f3a232afef428d11859963d78f09493 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 18 Dec 2025 20:04:39 +0100 Subject: [PATCH 2/4] svelte: Port `StatsValue` component from Ember.js app --- .../frontpage/StatsValue.stories.svelte | 23 ++++++++ .../components/frontpage/StatsValue.svelte | 52 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 svelte/src/lib/components/frontpage/StatsValue.stories.svelte create mode 100644 svelte/src/lib/components/frontpage/StatsValue.svelte diff --git a/svelte/src/lib/components/frontpage/StatsValue.stories.svelte b/svelte/src/lib/components/frontpage/StatsValue.stories.svelte new file mode 100644 index 0000000000..f01a88c5a4 --- /dev/null +++ b/svelte/src/lib/components/frontpage/StatsValue.stories.svelte @@ -0,0 +1,23 @@ + + + + + diff --git a/svelte/src/lib/components/frontpage/StatsValue.svelte b/svelte/src/lib/components/frontpage/StatsValue.svelte new file mode 100644 index 0000000000..1e4c7160cd --- /dev/null +++ b/svelte/src/lib/components/frontpage/StatsValue.svelte @@ -0,0 +1,52 @@ + + +
+ {value} + {label} +
+ + From 113dd737abe24fd2600e0c0027a33ef944d1d717 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 18 Dec 2025 20:43:46 +0100 Subject: [PATCH 3/4] svelte: Port `frontpage/ListItem` components from Ember.js app --- .../frontpage/ListItem.stories.svelte | 27 ++++++ .../lib/components/frontpage/ListItem.svelte | 89 +++++++++++++++++++ .../ListItemPlaceholder.stories.svelte | 15 ++++ .../frontpage/ListItemPlaceholder.svelte | 62 +++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 svelte/src/lib/components/frontpage/ListItem.stories.svelte create mode 100644 svelte/src/lib/components/frontpage/ListItem.svelte create mode 100644 svelte/src/lib/components/frontpage/ListItemPlaceholder.stories.svelte create mode 100644 svelte/src/lib/components/frontpage/ListItemPlaceholder.svelte diff --git a/svelte/src/lib/components/frontpage/ListItem.stories.svelte b/svelte/src/lib/components/frontpage/ListItem.stories.svelte new file mode 100644 index 0000000000..e0466b456f --- /dev/null +++ b/svelte/src/lib/components/frontpage/ListItem.stories.svelte @@ -0,0 +1,27 @@ + + + + + + + diff --git a/svelte/src/lib/components/frontpage/ListItem.svelte b/svelte/src/lib/components/frontpage/ListItem.svelte new file mode 100644 index 0000000000..5b03cfb079 --- /dev/null +++ b/svelte/src/lib/components/frontpage/ListItem.svelte @@ -0,0 +1,89 @@ + + + + +
+
{title}
+ {#if subtitle}
{subtitle}
{/if} +
+ +
+ + diff --git a/svelte/src/lib/components/frontpage/ListItemPlaceholder.stories.svelte b/svelte/src/lib/components/frontpage/ListItemPlaceholder.stories.svelte new file mode 100644 index 0000000000..63f2dd2a53 --- /dev/null +++ b/svelte/src/lib/components/frontpage/ListItemPlaceholder.stories.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/svelte/src/lib/components/frontpage/ListItemPlaceholder.svelte b/svelte/src/lib/components/frontpage/ListItemPlaceholder.svelte new file mode 100644 index 0000000000..45e0255c0b --- /dev/null +++ b/svelte/src/lib/components/frontpage/ListItemPlaceholder.svelte @@ -0,0 +1,62 @@ + + +
+
+ + {#if withSubtitle} + + {/if} +
+ +
+ + From d2826e1c2bfc78ed8677d777e56d91ddf3a40cd6 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Wed, 17 Dec 2025 13:30:54 +0100 Subject: [PATCH 4/4] svelte: Port index page from Ember.js app --- .../frontpage/CrateLists.stories.svelte | 91 +++++++++ .../components/frontpage/CrateLists.svelte | 189 ++++++++++++++++++ .../frontpage/ErrorState.stories.svelte | 15 ++ .../components/frontpage/ErrorState.svelte | 38 ++++ .../frontpage/HeroButtons.stories.svelte | 13 ++ .../components/frontpage/HeroButtons.svelte | 36 ++++ .../frontpage/IntroBlurb.stories.svelte | 29 +++ .../components/frontpage/IntroBlurb.svelte | 67 +++++++ svelte/src/routes/+page.svelte | 42 +++- svelte/src/routes/+page.ts | 38 ++++ svelte/src/routes/page.svelte.spec.ts | 14 -- 11 files changed, 554 insertions(+), 18 deletions(-) create mode 100644 svelte/src/lib/components/frontpage/CrateLists.stories.svelte create mode 100644 svelte/src/lib/components/frontpage/CrateLists.svelte create mode 100644 svelte/src/lib/components/frontpage/ErrorState.stories.svelte create mode 100644 svelte/src/lib/components/frontpage/ErrorState.svelte create mode 100644 svelte/src/lib/components/frontpage/HeroButtons.stories.svelte create mode 100644 svelte/src/lib/components/frontpage/HeroButtons.svelte create mode 100644 svelte/src/lib/components/frontpage/IntroBlurb.stories.svelte create mode 100644 svelte/src/lib/components/frontpage/IntroBlurb.svelte create mode 100644 svelte/src/routes/+page.ts delete mode 100644 svelte/src/routes/page.svelte.spec.ts diff --git a/svelte/src/lib/components/frontpage/CrateLists.stories.svelte b/svelte/src/lib/components/frontpage/CrateLists.stories.svelte new file mode 100644 index 0000000000..f7f3ab3f41 --- /dev/null +++ b/svelte/src/lib/components/frontpage/CrateLists.stories.svelte @@ -0,0 +1,91 @@ + + + + + diff --git a/svelte/src/lib/components/frontpage/CrateLists.svelte b/svelte/src/lib/components/frontpage/CrateLists.svelte new file mode 100644 index 0000000000..d323904884 --- /dev/null +++ b/svelte/src/lib/components/frontpage/CrateLists.svelte @@ -0,0 +1,189 @@ + + +
+
+ +

New Crates

+
    + {#if !summary} + {#each { length: 10 } as _, i (i)} +
  1. + {/each} + {:else} + {#each summary.new_crates as crate, index (crate.id)} +
  2. + +
  3. + {/each} + {/if} +
+
+ +
+ +

Most Downloaded

+
    + {#if !summary} + {#each { length: 10 } as _, i (i)} +
  1. + {/each} + {:else} + {#each summary.most_downloaded as crate, index (crate.id)} +
  2. + +
  3. + {/each} + {/if} +
+
+ +
+ +

Just Updated

+
    + {#if !summary} + {#each { length: 10 } as _, i (i)} +
  1. + {/each} + {:else} + {#each summary.just_updated as crate, index (crate.id)} +
  2. + +
  3. + {/each} + {/if} +
+
+ +
+ +

Most Recent Downloads

+
    + {#if !summary} + {#each { length: 10 } as _, i (i)} +
  1. + {/each} + {:else} + {#each summary.most_recently_downloaded as crate, index (crate.id)} +
  2. + +
  3. + {/each} + {/if} +
+
+ +
+

Popular Keywords

+
    + {#if !summary} + {#each { length: 10 } as _, i (i)} +
  • + {/each} + {:else} + {#each summary.popular_keywords as keyword (keyword.id)} +
  • + +
  • + {/each} + {/if} +
+
+ +
+

Popular Categories

+
    + {#if !summary} + {#each { length: 10 } as _, i (i)} +
  • + {/each} + {:else} + {#each summary.popular_categories as category (category.id)} +
  • + +
  • + {/each} + {/if} +
+
+
+ + diff --git a/svelte/src/lib/components/frontpage/ErrorState.stories.svelte b/svelte/src/lib/components/frontpage/ErrorState.stories.svelte new file mode 100644 index 0000000000..2c642b9c24 --- /dev/null +++ b/svelte/src/lib/components/frontpage/ErrorState.stories.svelte @@ -0,0 +1,15 @@ + + + alert('Retry clicked!') }} /> + + diff --git a/svelte/src/lib/components/frontpage/ErrorState.svelte b/svelte/src/lib/components/frontpage/ErrorState.svelte new file mode 100644 index 0000000000..f762281d47 --- /dev/null +++ b/svelte/src/lib/components/frontpage/ErrorState.svelte @@ -0,0 +1,38 @@ + + +

+ Unfortunately something went wrong while loading the crates.io summary data. Feel free to try again, or let the + crates.io team + know if the problem persists. +

+ + + + diff --git a/svelte/src/lib/components/frontpage/HeroButtons.stories.svelte b/svelte/src/lib/components/frontpage/HeroButtons.stories.svelte new file mode 100644 index 0000000000..d2d6170b91 --- /dev/null +++ b/svelte/src/lib/components/frontpage/HeroButtons.stories.svelte @@ -0,0 +1,13 @@ + + + diff --git a/svelte/src/lib/components/frontpage/HeroButtons.svelte b/svelte/src/lib/components/frontpage/HeroButtons.svelte new file mode 100644 index 0000000000..48d9a80750 --- /dev/null +++ b/svelte/src/lib/components/frontpage/HeroButtons.svelte @@ -0,0 +1,36 @@ + + + + + diff --git a/svelte/src/lib/components/frontpage/IntroBlurb.stories.svelte b/svelte/src/lib/components/frontpage/IntroBlurb.stories.svelte new file mode 100644 index 0000000000..1e692ecbe5 --- /dev/null +++ b/svelte/src/lib/components/frontpage/IntroBlurb.stories.svelte @@ -0,0 +1,29 @@ + + + + + diff --git a/svelte/src/lib/components/frontpage/IntroBlurb.svelte b/svelte/src/lib/components/frontpage/IntroBlurb.svelte new file mode 100644 index 0000000000..c01dc0b48a --- /dev/null +++ b/svelte/src/lib/components/frontpage/IntroBlurb.svelte @@ -0,0 +1,67 @@ + + +
+
+ Instantly publish your crates and install them. Use the API to interact and find out more information about + available crates. Become a contributor and enhance the site with your work. +
+ +
+ + +
+
+ + diff --git a/svelte/src/routes/+page.svelte b/svelte/src/routes/+page.svelte index 1a0164136a..f259ddd9f9 100644 --- a/svelte/src/routes/+page.svelte +++ b/svelte/src/routes/+page.svelte @@ -1,4 +1,38 @@ -

Welcome to SvelteKit

-

- Visit svelte.dev/docs/kit to read the documentation -

+ + + + +{#await data.summary} + + {#if isFirstLoad} + + + {:else} + + + {/if} +{:then summary} + + +{:catch _error} + + +{/await} diff --git a/svelte/src/routes/+page.ts b/svelte/src/routes/+page.ts new file mode 100644 index 0000000000..bd4bf7f323 --- /dev/null +++ b/svelte/src/routes/+page.ts @@ -0,0 +1,38 @@ +import type { operations } from '@crates-io/api-client'; + +import { browser } from '$app/environment'; +import { createClient } from '@crates-io/api-client'; + +type SummaryResponse = operations['get_summary']['responses']['200']['content']['application/json']; + +let cachedSummary: SummaryResponse | undefined; + +/** + * Load function to fetch summary data for the page. + * + * The summary data is cached on the client side to avoid redundant network requests + * and is streamed to the page instead of waiting for the entire data to be fetched + * before rendering. + */ +export async function load({ fetch }) { + const client = createClient({ fetch }); + + return { summary: fetchSummary(client) }; +} + +async function fetchSummary(client: ReturnType): Promise { + if (browser && cachedSummary) { + return cachedSummary; + } + + const response = await client.GET('/api/v1/summary'); + if (response.error) { + throw new Error('Failed to fetch summary data'); + } + + if (browser) { + cachedSummary = response.data; + } + + return response.data; +} diff --git a/svelte/src/routes/page.svelte.spec.ts b/svelte/src/routes/page.svelte.spec.ts deleted file mode 100644 index 8069a8bc1e..0000000000 --- a/svelte/src/routes/page.svelte.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { render } from 'vitest-browser-svelte'; -import { page } from 'vitest/browser'; - -import Page from './+page.svelte'; - -describe('/+page.svelte', () => { - it('should render h1', async () => { - render(Page); - - const heading = page.getByRole('heading', { level: 1 }); - await expect.element(heading).toBeInTheDocument(); - }); -});