feat(website): home page

This commit is contained in:
LukaDev 2024-08-13 14:53:57 +02:00
parent ac210ef24e
commit a2b6296f2e
15 changed files with 302 additions and 43 deletions

Binary file not shown.

View file

@ -1,33 +1,33 @@
import js from '@eslint/js';
import ts from 'typescript-eslint';
import svelte from 'eslint-plugin-svelte';
import prettier from 'eslint-config-prettier';
import globals from 'globals';
import js from "@eslint/js"
import ts from "typescript-eslint"
import svelte from "eslint-plugin-svelte"
import prettier from "eslint-config-prettier"
import globals from "globals"
/** @type {import('eslint').Linter.Config[]} */
export default [
js.configs.recommended,
...ts.configs.recommended,
...svelte.configs['flat/recommended'],
...svelte.configs["flat/recommended"],
prettier,
...svelte.configs['flat/prettier'],
...svelte.configs["flat/prettier"],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
...globals.node,
},
},
},
{
files: ['**/*.svelte'],
files: ["**/*.svelte"],
languageOptions: {
parserOptions: {
parser: ts.parser
}
}
parser: ts.parser,
},
},
},
{
ignores: ['build/', '.svelte-kit/', 'dist/']
}
];
ignores: ["build/", ".svelte-kit/", "dist/"],
},
]

View file

@ -33,5 +33,9 @@
"typescript-eslint": "^8.0.0",
"vite": "^5.0.3"
},
"type": "module"
"type": "module",
"dependencies": {
"@fontsource-variable/nunito-sans": "^5.0.14",
"date-fns": "^3.6.0"
}
}

View file

@ -1,6 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
autoprefixer: {},
},
}

View file

@ -1,3 +1,45 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
:root {
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: 10 7 4;
--color-card: 28 22 17;
--color-card-hover: 40 32 25;
--color-body: 198 167 140;
--color-heading: 227 213 200;
--color-light: 255 255 255;
--color-input-bg: 22 15 8;
--color-input-border: 82 60 41;
--color-placeholder: 169 147 128;
--color-primary: 241 157 30;
--color-primary-hover: 255 172 42;
--color-primary-fg: var(--color-background);
}
}
html {
scroll-padding-top: theme(spacing.16);
}
body {
background-color: theme(colors.background);
color: theme(colors.body);
}
@keyframes cursor-blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}

View file

@ -10,4 +10,4 @@ declare global {
}
}
export {};
export {}

View file

@ -0,0 +1,31 @@
import { PUBLIC_REGISTRY_URL } from "$env/static/public"
export type PackageResponse = {
name: string
version: string
target: TargetInfo
description: string
published_at: string
license: string
}
export type TargetInfo = {
kind: TargetKind
lib: boolean
bin: boolean
}
export type TargetKind = "roblox" | "lune" | "luau"
export async function fetchRegistry<T>(
path: string,
fetcher: typeof fetch,
options?: RequestInit,
): Promise<T> {
const response = await fetcher(new URL(path, PUBLIC_REGISTRY_URL), options)
if (!response.ok) {
throw new Error(`Failed to fetch from registry: ${response.status} ${response.statusText}`)
}
return response.json()
}

View file

@ -0,0 +1,9 @@
import { ISR_BYPASS_TOKEN } from "$env/static/private"
import type { Config } from "@sveltejs/adapter-vercel"
export const config: Config = {
isr: {
expiration: 30 * 60,
bypassToken: ISR_BYPASS_TOKEN,
},
}

View file

@ -1,4 +1,5 @@
<script>
import '@fontsource-variable/nunito-sans';
import '../app.css';
</script>

View file

@ -0,0 +1,8 @@
import { fetchRegistry, type PackageResponse } from "$lib/registry-api"
import type { PageServerLoad } from "./$types"
export const load: PageServerLoad = async ({ fetch }) => {
const packages = await fetchRegistry<PackageResponse[]>("search", fetch)
return { packages }
}

View file

@ -1,2 +1,40 @@
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
<script lang="ts">
import type { PageData } from "./$types"
import { formatDistanceToNow } from "date-fns"
import Hero from "./Hero.svelte"
export let data: PageData
</script>
<Hero />
<section class="mx-auto max-w-screen-xl px-4 pb-32">
<h2 class="mb-4 text-2xl font-semibold text-heading">
<a id="recently-published" href="#recently-published">Recently Published</a>
</h2>
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{#each data.packages as pkg}
{@const [scope, name] = pkg.name.split("/")}
{@const targetName = pkg.target.kind[0].toUpperCase() + pkg.target.kind.slice(1)}
<article
class="hover:bg-card-hover relative overflow-hidden rounded bg-card px-5 py-4 transition"
>
<h3 class="text-xl font-semibold">
<a href={`/packages/${pkg.name}`} class="after:absolute after:inset-0 after:content-['']">
<span class="text-heading">{scope}/</span><span class="text-light">{name}</span>
</a>
</h3>
<div class="mb-3 text-sm font-semibold text-primary">v{pkg.version} · {targetName}</div>
<p class="mb-3 line-clamp-2 h-[2lh] overflow-hidden text-sm">{pkg.description}</p>
<div class="text-sm font-semibold text-heading">
<time datetime={pkg.published_at}>
{formatDistanceToNow(new Date(pkg.published_at), { addSuffix: true })}
</time>
</div>
</article>
{/each}
</div>
</section>

View file

@ -0,0 +1,90 @@
<script lang="ts">
import { onMount } from "svelte"
export const prerender = true
const tools = ["Luau", "Roblox", "Lune"]
let typewriteText = $state("Luau")
let blink = $state(true)
let cursorVisible = $state(false)
onMount(() => {
let current = 0
let timeout: number
function typewrite(text: string) {
blink = false
let progress = 0
timeout = setInterval(() => {
progress++
typewriteText = text.slice(0, progress)
if (progress >= text.length) {
blink = true
clearInterval(timeout)
timeout = setTimeout(() => clear(), 3500)
}
}, 120)
}
function clear() {
blink = false
let progress = typewriteText.length
timeout = setInterval(() => {
progress--
typewriteText = typewriteText.slice(0, progress)
if (progress <= 0) {
clearInterval(timeout)
timeout = setTimeout(() => {
current++
if (current >= tools.length) current = 0
typewrite(tools[current])
}, 1000)
}
}, 80)
}
cursorVisible = true
timeout = setTimeout(() => {
clear()
}, 4500)
return () => {
clearTimeout(timeout)
}
})
</script>
<section class="mx-auto max-w-screen-xl px-4 py-32">
<h1 class="mb-6 text-5xl font-semibold text-heading">
Manage your packages<br />
<span class="sr-only"> for Luau</span>
<span class="text-primary" aria-hidden="true">
for {typewriteText}{#if cursorVisible}
<span
class="ml-1 inline-block h-9 w-0.5 bg-current duration-100"
class:animate-cursor-blink={blink}
></span>
{/if}
</span>
</h1>
<p class="mb-8 max-w-md text-lg">
A package manager for the Luau programming language, supporting multiple runtimes including
Roblox and Lune.
</p>
<a
href="#"
class="hover:bg-primary-hover inline-flex h-11 items-center rounded bg-primary px-5 font-semibold text-primary-fg transition"
>
Get Started
</a>
</section>

View file

@ -1,16 +1,16 @@
import { mdsvex } from 'mdsvex';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import adapter from '@sveltejs/adapter-vercel';
import { mdsvex } from "mdsvex"
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"
import adapter from "@sveltejs/adapter-vercel"
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: [vitePreprocess(), mdsvex()],
kit: {
adapter: adapter({})
adapter: adapter({}),
},
extensions: ['.svelte', '.svx']
};
extensions: [".svelte", ".svx"],
}
export default config;
export default config

View file

@ -1,12 +1,48 @@
import type { Config } from 'tailwindcss';
import type { Config } from "tailwindcss"
import defaultTheme from "tailwindcss/defaultTheme"
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
content: ["./src/**/*.{html,js,svelte,ts}"],
theme: {
extend: {}
extend: {
fontFamily: {
sans: ["Nunito Sans Variable", ...defaultTheme.fontFamily.sans],
},
colors: {
background: "rgb(var(--color-background) / <alpha-value>)",
card: {
DEFAULT: "rgb(var(--color-card) / <alpha-value>)",
hover: "rgb(var(--color-card-hover) / <alpha-value>)",
},
body: "rgb(var(--color-body) / <alpha-value>)",
heading: "rgb(var(--color-heading) / <alpha-value>)",
light: "rgb(var(--color-light) / <alpha-value>)",
input: {
bg: "rgb(var(--color-input-bg) / <alpha-value>)",
border: "rgb(var(--color-input-border) / <alpha-value>)",
},
placeholder: "rgb(var(--color-placeholder) / <alpha-value>)",
primary: {
DEFAULT: "rgb(var(--color-primary) / <alpha-value>)",
hover: "rgb(var(--color-primary-hover) / <alpha-value>)",
fg: "rgb(var(--color-primary-fg) / <alpha-value>)",
},
},
animation: {
"cursor-blink": "cursor-blink 1s ease-in-out 500ms infinite",
},
borderRadius: {
none: "0",
sm: `${4 / 16}rem`,
DEFAULT: `${8 / 16}rem`,
},
},
},
// eslint-disable-next-line @typescript-eslint/no-require-imports
plugins: [require('@tailwindcss/typography')]
} as Config;
plugins: [require("@tailwindcss/typography")],
} as Config

View file

@ -1,6 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { sveltekit } from "@sveltejs/kit/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [sveltekit()]
});
plugins: [sveltekit()],
})