mirror of
https://github.com/pesde-pkg/pesde.git
synced 2025-05-04 10:33:47 +01:00
feat(website): home page
This commit is contained in:
parent
ac210ef24e
commit
a2b6296f2e
15 changed files with 302 additions and 43 deletions
Binary file not shown.
|
@ -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/"],
|
||||
},
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
2
website/src/app.d.ts
vendored
2
website/src/app.d.ts
vendored
|
@ -10,4 +10,4 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
export {}
|
||||
|
|
31
website/src/lib/registry-api.ts
Normal file
31
website/src/lib/registry-api.ts
Normal 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()
|
||||
}
|
9
website/src/routes/+layout.server.ts
Normal file
9
website/src/routes/+layout.server.ts
Normal 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,
|
||||
},
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import '@fontsource-variable/nunito-sans';
|
||||
import '../app.css';
|
||||
</script>
|
||||
|
||||
|
|
8
website/src/routes/+page.server.ts
Normal file
8
website/src/routes/+page.server.ts
Normal 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 }
|
||||
}
|
|
@ -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>
|
||||
|
|
90
website/src/routes/Hero.svelte
Normal file
90
website/src/routes/Hero.svelte
Normal 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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()],
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue