feat(website): 🚀 create website

This commit is contained in:
daimond113 2024-03-16 17:28:20 +01:00
parent 9c806ba082
commit e3b21315ff
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
38 changed files with 3998 additions and 0 deletions

9
website/.eslintignore Normal file
View file

@ -0,0 +1,9 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
pnpm-lock.yaml

31
website/.eslintrc.cjs Normal file
View file

@ -0,0 +1,31 @@
/** @type { import("eslint").Linter.Config } */
module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended',
'prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2017: true,
node: true
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
]
};

10
website/.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

1
website/.npmrc Normal file
View file

@ -0,0 +1 @@
engine-strict=true

4
website/.prettierignore Normal file
View file

@ -0,0 +1,4 @@
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

17
website/.prettierrc Normal file
View file

@ -0,0 +1,17 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": [
"prettier-plugin-svelte"
],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}

59
website/package.json Normal file
View file

@ -0,0 +1,59 @@
{
"name": "website",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
},
"devDependencies": {
"@shikijs/markdown-it": "^1.1.7",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/eslint": "^8.56.0",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"autoprefixer": "^10.4.18",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.35.1",
"postcss": "^8.4.35",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"tailwindcss": "^3.4.1",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^5.0.3"
},
"type": "module",
"dependencies": {
"@fontsource-variable/hepta-slab": "^5.0.19",
"@tailwindcss/typography": "^0.5.10",
"@types/markdown-it": "^13.0.7",
"@types/pako": "^2.0.3",
"@types/tar-stream": "^3.1.3",
"events": "^3.3.0",
"isomorphic-dompurify": "^2.4.0",
"lucide-svelte": "^0.358.0",
"markdown-it": "^14.0.0",
"pako": "^2.1.0",
"shiki": "^1.1.7",
"simple-svelte-autocomplete": "^2.5.2",
"tar-stream": "^3.1.7",
"yaml": "^2.4.1"
},
"pnpm": {
"patchedDependencies": {
"tar-stream@3.1.7": "patches/tar-stream@3.1.7.patch",
"simple-svelte-autocomplete@2.5.2": "patches/simple-svelte-autocomplete@2.5.2.patch"
}
}
}

View file

@ -0,0 +1,16 @@
diff --git a/package.json b/package.json
index 0a796615e65323624ae9a1fdcc7c831f39dc5158..ac84dd37cf4c95223f2727d4522b8ceb74a48dff 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,11 @@
"svelte": "src/SimpleAutocomplete.svelte",
"module": "dist/index.mjs",
"main": "dist/index.js",
+ "exports": {
+ ".": {
+ "svelte": "./src/SimpleAutocomplete.svelte"
+ }
+ },
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/preset-env": "^7.16.11",

View file

@ -0,0 +1,9 @@
diff --git a/extract.js b/extract.js
index 0ed9f82bf287aa040dd560eabbe052316223011d..16f26d49a8b0eb554b7e0a79a076027612fb4ce1 100644
--- a/extract.js
+++ b/extract.js
@@ -1,3 +1,4 @@
+const EventEmitter = require('events')
const { Writable, Readable, getStreamError } = require('streamx')
const FIFO = require('fast-fifo')
const b4a = require('b4a')

2925
website/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

View file

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

19
website/src/app.css Normal file
View file

@ -0,0 +1,19 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
html {
@apply font-serif;
}
}
@layer utilities {
.overflow-text {
@apply min-w-0 whitespace-nowrap overflow-hidden text-ellipsis;
}
}
a {
@apply text-links underline;
}

13
website/src/app.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

21
website/src/app.html Normal file
View file

@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#ffa360">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#f8e4d5">
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover" class="bg-main-background text-standard-text h-screen">
<div class="contents">%sveltekit.body%</div>
</body>
</html>

92
website/src/autocomplete.d.ts vendored Normal file
View file

@ -0,0 +1,92 @@
// // https://github.com/pstanoev/simple-svelte-autocomplete/issues/205#issuecomment-1960396289
declare module 'simple-svelte-autocomplete' {
import { SvelteComponent } from 'svelte';
import { HTMLAttributes } from 'svelte/elements';
export interface AutoCompleteAttributes<T> extends HTMLAttributes<HTMLDivElement> {
autocompleteOffValue?: string;
className?: string;
cleanUserText?: boolean;
closeOnBlur?: boolean;
create?: boolean;
createText?: string;
delay?: number;
disabled?: boolean;
dropdownClassName?: string;
flag?: boolean;
hideArrow?: boolean;
highlightedItem?: T;
html5autocomplete?: boolean;
ignoreAccents?: boolean;
inputClassName?: string;
inputId?: string;
items?: T[];
keywordsFieldName?: string;
labelFieldName?: string;
localFiltering?: boolean;
localSorting?: boolean;
lock?: boolean;
lowercaseKeywords?: boolean;
matchAllKeywords?: boolean;
maxItemsToShowInList?: number;
minCharactersToSearch?: number;
moreItemsText?: string;
multiple?: boolean;
name?: string;
noInputClassName?: boolean;
noInputStyles?: boolean;
noResultsText?: string;
orderableSection?: boolean;
placeholder?: string;
readonly?: boolean;
required?: boolean;
selectFirstIfEmpty?: boolean;
selectName?: string;
selectedItem?: T;
showClear?: boolean;
showLoadingIndicator?: boolean;
sortByMatchedKeywords?: boolean;
tabIndex?: number;
value?: T;
valueFieldName?: string;
}
export interface AutoCompleteFunctions<T> {
itemFilterFunction?: (item: T, keywords: string) => boolean;
itemSortFunction?: (item1: T, item2: T, keywords: string) => number;
keywordsCleanFunction?: (keywords: string) => string;
keywordsFunction?: (item: T) => string;
labelFunction?: (item: T) => string;
searchFunction?: (keyword: string, maxItemsToShowInList: number) => Promise<T[]> | boolean;
textCleanFunction?: (string) => string;
valueFunction?: (a: T) => string;
}
export interface AutoCompleteCallbacks<T> {
beforeChange?: (oldSelectedItem: T, newSelectedItem: T) => boolean;
onChange?: (newSelectedItem: T) => void;
onFocus?: () => void;
onBlur?: () => void;
onCreate?: (text: string) => void;
}
export interface AutoCompleteSlots<T> {
item: { item: T; label: string };
'no-results': null;
loading: { loadingText: string };
tag: null;
'dropdown-header': { nbItems: number; maxItemsToShowInList: number };
'dropdown-footer': { nbItems: number; maxItemsToShowInList: number };
}
export interface AutoCompleteProps<T>
extends AutoCompleteAttributes<T>,
AutoCompleteCallbacks<T>,
AutoCompleteFunctions<T> {}
export default class AutoComplete<T> extends SvelteComponent<
AutoCompleteProps<T>,
undefined,
AutoCompleteSlots<T>
> {}
}

View file

@ -0,0 +1,14 @@
<script context="module">
import { codeToHtml } from 'shiki';
</script>
<script lang="ts">
export let lang = 'bash';
export let code: string;
</script>
{#await codeToHtml(code, { theme: 'vesper', lang, transformers: [{ pre(node) {
this.addClassToHast(node, 'not-prose');
} }] }) then highlightedCode}
{@html highlightedCode}
{/await}

View file

@ -0,0 +1,18 @@
import MarkdownIt from 'markdown-it';
import Shiki from '@shikijs/markdown-it';
import { writable } from 'svelte/store';
// nasty hack to get around the fact that @shikijs/markdown-it is async
export const md = writable<MarkdownIt | undefined>(undefined);
const it = MarkdownIt({
html: true
});
Promise.all([Shiki({ theme: 'vesper' })]).then((plugins) => {
for (const plugin of plugins) {
it.use(plugin);
}
md.set(it);
});

View file

@ -0,0 +1,131 @@
<script lang="ts">
import { goto } from '$app/navigation';
import '../app.css';
import '@fontsource-variable/hepta-slab';
import Autocomplete from 'simple-svelte-autocomplete';
import Menu from 'lucide-svelte/icons/menu';
import { onMount } from 'svelte';
type SearchItem = {
name: string;
version: string;
description: string;
};
const fetchSearchData = async (query: string) => {
const request = await fetch(
`${import.meta.env.VITE_API_URL}/v0/search?query=${encodeURIComponent(query)}`
);
return (await request.json()) as SearchItem[];
};
let selectedSearchItem: SearchItem | null = null;
$: {
if (selectedSearchItem) {
goto(`/packages/${selectedSearchItem.name}/${selectedSearchItem.version}`);
}
}
let linksOpen = false;
let linksRef: HTMLDivElement;
onMount(() => {
const handleClick = (event: MouseEvent) => {
if (linksOpen && !linksRef.contains(event.target as Node)) {
linksOpen = false;
}
};
document.addEventListener('click', handleClick);
return () => {
document.removeEventListener('click', handleClick);
};
});
</script>
<div class="flex flex-col px-8 lg:px-16 py-4 gap-8 items-center lg:*:max-w-6xl">
<header
class="flex-0 flex flex-col lg:flex-row relative items-center gap-4 lg:gap-0 min-h-12 w-full"
>
<div class="flex items-center gap-8">
<a href="/" class="inline-block lg:absolute top-0 left-0">
<img src="/logo.svg" alt="pesde" class="h-12" />
</a>
<div
class="relative lg:absolute lg:right-0 lg:top-1/2 lg:-translate-y-1/2 flex items-center"
bind:this={linksRef}
>
<button
type="button"
title="Toggle links"
class="hover:brightness-110 transition-[filter]"
on:click={() => {
linksOpen = !linksOpen;
}}
>
<Menu class="size-8" />
</button>
<div
class="absolute top-8 right-0 bg-paper-1-alt z-10 flex flex-col gap-4 px-2 py-4 rounded-md *:no-underline *:text-standard-text hover:*:brightness-110 *:max-w-60 *:w-max"
class:hidden={!linksOpen}
>
<a href="https://github.com/daimond113/pesde">GitHub Repository</a>
<a href="/policies">Policies</a>
</div>
</div>
</div>
<Autocomplete
inputClassName="mx-auto rounded-full text-white placeholder:opacity-75 placeholder:text-white bg-paper-1 px-3 py-1 w-full h-8 hover:brightness-110 transition-[filter]"
dropdownClassName="!bg-paper-1-alt !border-none rounded-md not-prose !p-2"
placeholder="search"
searchFunction={fetchSearchData}
delay={350}
localFiltering={false}
labelFieldName="name"
valueFieldName="name"
bind:selectedItem={selectedSearchItem}
hideArrow={true}
>
<div slot="item" let:item>
<div
class="flex flex-col justify-center w-full no-underline text-standard-text transition-[filter] h-16"
>
<div class="font-bold text-lg overflow-text">{item?.name}</div>
{#if item?.description}
<div class="overflow-text">
{item.description}
</div>
{/if}
</div>
</div>
</Autocomplete>
</header>
<div class="prose prose-pesde w-full flex-1 flex-shrink-0">
<slot />
</div>
</div>
<style>
:global(.autocomplete) {
margin-left: auto;
margin-right: auto;
max-width: 25rem !important;
}
:global(.autocomplete-list-item) {
background: #4c3c2d !important;
color: unset !important;
}
:global(.autocomplete-list-item):hover {
filter: brightness(1.1);
}
:global(.autocomplete-list-item-no-results) {
color: unset !important;
}
</style>

View file

@ -0,0 +1,54 @@
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<svelte:head>
<title>pesde</title>
</svelte:head>
<section
class="flex flex-col items-center lg:items-start text-center lg:text-start not-prose gap-6 my-4"
>
<h1 class="text-3xl font-extrabold text-balance">
pesde - the feature-rich Roblox package manager
</h1>
<div class="text-xl font-medium text-balance">
pesde is a package manager for Roblox that is designed to be feature-rich and easy to use.
</div>
<div>
<a
href="https://github.com/daimond113/pesde?tab=readme-ov-file#installation"
class="text-standard-text no-underline rounded-md px-4 py-2 bg-paper-1 inline-block"
>Install</a
>
<a
href="https://github.com/daimond113/pesde?tab=readme-ov-file#preparing-to-publish"
class="text-standard-text no-underline rounded-md px-4 py-2 bg-paper-1-alt inline-block"
>Publish</a
>
</div>
</section>
<section>
<h2>Recently published packages</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2 overflow-auto">
{#each data.latest as latestPackage}
<a
href="/packages/{latestPackage.name}/{latestPackage.version}"
class="flex flex-col justify-center p-4 bg-paper-1 rounded-md text-standard-text no-underline not-prose h-32 *:overflow-text hover:brightness-110 transition-[filter]"
>
<span class="text-sm"
>Published at <time datetime={latestPackage.published_at.toISOString()}
>{latestPackage.published_at.toLocaleString()}</time
></span
>
<div class="text-lg font-medium">{latestPackage.name}@{latestPackage.version}</div>
{#if latestPackage.description}
<div>{latestPackage.description}</div>
{/if}
</a>
{/each}
</div>
</section>

View file

@ -0,0 +1,26 @@
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const ssr = false;
export const load: PageLoad = async ({ fetch }) => {
const latestRes = await fetch(`${import.meta.env.VITE_API_URL}/v0/search`);
if (!latestRes.ok) {
error(latestRes.status, await latestRes.text());
}
const latest = (await latestRes.json()) as {
name: string;
version: string;
description?: string;
published_at: string;
}[];
return {
latest: latest.map((pkg) => ({
...pkg,
published_at: new Date(parseInt(pkg.published_at) * 1000)
}))
};
};

View file

@ -0,0 +1,194 @@
<script context="module">
import DOMPurify from 'isomorphic-dompurify';
DOMPurify.addHook('afterSanitizeAttributes', function (node) {
if (node.tagName === 'A') {
node.setAttribute('target', '_blank');
node.setAttribute('rel', 'noopener noreferrer');
}
});
</script>
<script lang="ts">
import type { PageData } from './$types';
import { md } from '$lib/markdown';
import Codeblock from '$lib/Codeblock.svelte';
import { goto } from '$app/navigation';
import ChevronDown from 'lucide-svelte/icons/chevron-down';
import Mail from 'lucide-svelte/icons/mail';
import Globe from 'lucide-svelte/icons/globe';
export let data: PageData;
$: markdown =
data.readme &&
DOMPurify.sanitize($md?.render(data.readme) ?? '', {
FORBID_ATTR: ['src'],
FORBID_TAGS: [
'script',
'style',
'img',
'video',
'audio',
'iframe',
'object',
'embed',
'canvas',
'source'
]
});
const parseAuthor = (author: string) => {
const authorRegex =
/^(?<name>.+?)(?:\s*<(?<email>.+?)>)?(?:\s*\((?<url>.+?)\))?(?:\s*<(?<email2>.+?)>)?(?:\s*\((?<url2>.+?)\))?$/;
const { groups } = author.match(authorRegex) ?? {};
return {
name: groups?.name ?? author,
email: groups?.email ?? groups?.email2,
url: groups?.url ?? groups?.url2
};
};
$: publishedAt = new Date(
(data.versions.find(([version]) => version === data.version)?.[1] ?? 0) * 1000
);
$: allDependencies = [
[data.dependencies, 'Dependencies'],
[data.peerDependencies, 'Peer Dependencies']
] as const;
</script>
<svelte:head>
<title>{data.scope}/{data.name}@{data.version}</title>
</svelte:head>
<div class="flex flex-col lg:flex-row">
<div class="flex-shrink flex-grow pr-4">
<div class="mb-4">
<h1 class="mb-0">{data.scope}/{data.name}</h1>
{#if data.description}
<div class="lead mt-0 mb-0">{data.description}</div>
{/if}
</div>
<main>{@html markdown}</main>
</div>
<div class="w-full lg:w-72 flex-none">
<hr class="lg:hidden" />
<div class="flex flex-col gap-4 lg:sticky top-4">
<section>
<label for="version-select" class="section-title">Version</label>
<div class="relative">
<select
class="w-full h-full px-4 py-2 rounded-full bg-paper-1 text-standard-text appearance-none hover:brightness-110 transition-[filter]"
title="Version"
id="version-select"
on:change={(event) => {
goto(`/packages/${data.scope}/${data.name}/${event.target?.value}`);
}}
>
{#each data.versions as [version]}
<option value={version} selected={version === data.version}>{version}</option>
{/each}
</select>
<ChevronDown class="absolute right-4 top-1/4 pointer-events-none" />
</div>
</section>
<section>
<div class="section-title">Published at</div>
<div class="flex items-center gap-2">
<time datetime={publishedAt.toISOString()}>{publishedAt.toLocaleString()}</time>
</div>
</section>
<section>
<div class="section-title">Installation</div>
<Codeblock code="pesde add {data.scope}/{data.name}@{data.version}" />
</section>
{#if data.license}
<section>
<div class="section-title">License</div>
<div>{data.license}</div>
</section>
{/if}
{#if data.repository}
<section>
<div class="section-title">Repository</div>
<a
href={data.repository}
target="_blank"
rel="noopener noreferrer"
class="block overflow-text">{data.repository}</a
>
</section>
{/if}
{#if data.authors}
<section>
<div class="section-title">Authors</div>
<ul class="not-prose">
{#each data.authors as author}
{@const parsedAuthor = parseAuthor(author)}
<li class="flex">
<span class="overflow-text pr-2">
{parsedAuthor.name}
</span>
<div class="ml-auto flex items-center gap-4">
{#if parsedAuthor.email}
<a href="mailto:{parsedAuthor.email}" title="Email {parsedAuthor.name}">
<Mail class="size-6" />
</a>
{/if}
{#if parsedAuthor.url}
<a href={parsedAuthor.url} title="Website of {parsedAuthor.name}">
<Globe class="size-6" />
</a>
{/if}
</div>
</li>
{/each}
</ul>
</section>
{/if}
{#if data.realm}
<section>
<div class="section-title">Realm</div>
<div>{data.realm}</div>
</section>
{/if}
{#each allDependencies as [dependencies, title]}
{#if dependencies}
<section>
<div class="section-title">{title}</div>
<ul class="not-prose">
{#each dependencies as dependency}
<li>
{#if 'name' in dependency}
<a
href="/packages/{dependency.name}/latest"
class="block overflow-text"
title="View {dependency.name}"
>
{dependency.name}@{dependency.version}
</a>
{:else}
{@const url = /.+\/.+/.test(dependency.repo)
? `https://github.com/${dependency.repo}`
: dependency.repo}
<a href={url} class="block overflow-text" title="View {dependency.repo}">
{dependency.repo}#{dependency.rev}
</a>
{/if}
</li>
{/each}
</ul>
</section>
{/if}
{/each}
</div>
</div>
</div>
<style>
.section-title {
@apply text-xl font-semibold;
}
</style>

View file

@ -0,0 +1,114 @@
import { error, redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types';
import { extract } from 'tar-stream';
import { inflate } from 'pako';
import { parse } from 'yaml';
export const ssr = false;
type Dependencies = ({ name: string; version: string } | { repo: string; rev: string })[];
export const load: PageLoad = async ({ params, fetch }) => {
const res = await fetch(
`${import.meta.env.VITE_API_URL}/v0/packages/${params.scope}/${params.name}/${params.version}`
);
if (res.status === 404) {
error(res.status, 'Package not found');
} else if (!res.ok) {
error(res.status, await res.text());
}
const body = await res.arrayBuffer();
const extractStream = extract();
extractStream.end(inflate(body));
let manifestBuffer, readmeBuffer;
for await (const entry of extractStream) {
const read = () => {
return new Promise<Uint8Array>((resolve, reject) => {
const chunks: number[] = [];
entry.on('data', (chunk: Uint8Array) => {
chunks.push(...chunk);
});
entry.on('end', () => {
resolve(new Uint8Array(chunks));
});
entry.on('error', reject);
});
};
switch (entry.header.name.toLowerCase()) {
case 'pesde.yaml': {
manifestBuffer = await read();
break;
}
case 'readme.md':
case 'readme.txt':
case 'readme': {
readmeBuffer = await read();
break;
}
}
entry.resume();
}
if (!manifestBuffer) {
error(500, 'Package is missing pesde.yaml');
}
const textDecoder = new TextDecoder();
const manifest = textDecoder.decode(manifestBuffer);
const parsed = parse(manifest, {
customTags: [
{
tag: '!roblox',
collection: 'map'
}
]
}) as {
version: string;
authors?: string[];
description?: string;
license?: string;
repository?: string;
realm?: string;
dependencies?: Dependencies;
peer_dependencies?: Dependencies;
};
if (params.version.toLowerCase() === 'latest') {
redirect(302, `/packages/${params.scope}/${params.name}/${parsed.version}`);
}
const readme = readmeBuffer ? textDecoder.decode(readmeBuffer) : null;
const versionsRes = await fetch(
`${import.meta.env.VITE_API_URL}/v0/packages/${params.scope}/${params.name}/versions`
);
if (!versionsRes.ok) {
error(versionsRes.status, await versionsRes.text());
}
const versions = (await versionsRes.json()) as [string, number][];
return {
scope: params.scope,
name: params.name,
version: parsed.version,
versions,
authors: parsed.authors,
description: parsed.description,
license: parsed.license,
readme,
repository: parsed.repository,
realm: parsed.realm,
dependencies: parsed.dependencies,
peerDependencies: parsed.peer_dependencies
};
};

View file

@ -0,0 +1,68 @@
<svelte:head>
<title>Policies</title>
</svelte:head>
<div class="max-w-prose">
<h1>Policies for content on the public pesde registry</h1>
<p>
If anything is unclear, please <a href="mailto:pesde@daimond113.com">contact us</a> and we will be
happy to help.
</p>
<section>
<h2>Permitted content</h2>
<p>
The pesde registry is a place for open source Roblox packages. Examples of allowed content:
</p>
<ul>
<li>Libraries</li>
<li>Frameworks</li>
</ul>
Examples of disallowed content:
<ul>
<li>Malicious code</li>
<li>Illegal content</li>
</ul>
pesde is not responsible for the content of packages. If you believe a package is malicious or contains
illegal content, please
<a href="mailto:pesde@daimond113.com">contact us</a>.
</section>
<section>
<h2>Package removal</h2>
<p>
pesde does not support removing packages from the registry without a reason such as security
or complying with the law. In case you published a secret to the registry, you must regenerate
it. If you believe a package should be removed, please <a href="mailto:pesde@daimond113.com"
>contact us</a
>. We will review your request and take action if necessary.
</p>
<p>
If we find that a package is breaking the permitted content policy, we will remove it from the
registry without notice.
</p>
<p>
pesde reserves the right to remove any package from the registry at any time for any reason.
</p>
</section>
<section>
<h2>Package ownership</h2>
<p>
Packages are owned by scopes. The first person to publish to the scope owns the scope. If you
want to work as a team, the owner of the scope must send a pull request to the <a
href="https://github.com/daimond113/pesde-index">index repo</a
> adding the members' user IDs to the scope's `owners.yaml` file.
</p>
</section>
<section>
<h2>Scope squatting</h2>
<p>
Scope squatting is the act of creating a scope with the intent of preventing others from using
it. Scope squatting is not allowed. If you believe a scope is being squatted, please
<a href="mailto:pesde@daimond113.com">contact us</a>. We will review your request and take
action if necessary.
</p>
</section>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

BIN
website/static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

7
website/static/logo.svg Normal file
View file

@ -0,0 +1,7 @@
<svg viewBox="0 0 215 107" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 106.66V100.708H10.224V41.3322H0V35.3802H17.136V53.6682L16.944 59.1402V62.1642L17.136 66.4842V100.708H27.168V106.66H0ZM36.432 87.4602C32.208 87.4602 28.416 86.5322 25.056 84.6762C21.728 82.8202 18.976 80.1802 16.8 76.7562C14.624 73.3002 13.184 69.1722 12.48 64.3722L16.944 61.0602C16.944 64.9962 17.696 68.4842 19.2 71.5242C20.704 74.5322 22.864 76.9002 25.68 78.6282C28.496 80.3562 31.84 81.2202 35.712 81.2202C39.52 81.2202 42.832 80.3562 45.648 78.6282C48.464 76.9002 50.624 74.5162 52.128 71.4762C53.664 68.4042 54.432 64.9002 54.432 60.9642C54.432 56.9642 53.648 53.4442 52.08 50.4042C50.512 47.3642 48.32 44.9802 45.504 43.2522C42.688 41.4922 39.424 40.6122 35.712 40.6122C31.904 40.6122 28.592 41.4922 25.776 43.2522C22.96 44.9802 20.784 47.3802 19.248 50.4522C17.712 53.5242 16.944 57.0602 16.944 61.0602L15.84 50.1642H17.472C18.048 47.3482 19.168 44.7562 20.832 42.3882C22.496 39.9882 24.688 38.0682 27.408 36.6282C30.16 35.1562 33.424 34.4202 37.2 34.4202C40.784 34.4202 44.064 35.0762 47.04 36.3882C50.016 37.6682 52.576 39.5082 54.72 41.9082C56.896 44.2762 58.576 47.0762 59.76 50.3082C60.976 53.5402 61.584 57.0922 61.584 60.9642C61.584 66.1162 60.544 70.6922 58.464 74.6922C56.384 78.6922 53.456 81.8282 49.68 84.1002C45.936 86.3402 41.52 87.4602 36.432 87.4602Z" fill="#F8E4D5"/>
<path d="M72.912 59.9641C67.632 59.9641 63.056 58.8441 59.184 56.6041C55.312 54.3641 52.32 51.2761 50.208 47.3401C48.096 43.3721 47.04 38.8121 47.04 33.6601C47.04 28.4441 48.096 23.8361 50.208 19.8361C52.352 15.8361 55.328 12.7001 59.136 10.4281C62.976 8.15613 67.44 7.02013 72.528 7.02013C77.648 7.02013 82.08 8.14013 85.824 10.3801C89.568 12.6201 92.448 15.7241 94.464 19.6921C96.48 23.6601 97.488 28.2361 97.488 33.4201C97.488 34.0281 97.472 34.5081 97.44 34.8601C97.44 35.2121 97.408 35.5161 97.344 35.7721H90.528C90.56 35.3881 90.576 34.9721 90.576 34.5241C90.608 34.0441 90.624 33.5001 90.624 32.8921C90.624 29.0841 89.888 25.7081 88.416 22.7641C86.976 19.7881 84.912 17.4521 82.224 15.7561C79.536 14.0601 76.304 13.2121 72.528 13.2121C68.848 13.2121 65.616 14.0921 62.832 15.8521C60.048 17.5801 57.888 19.9801 56.352 23.0521C54.816 26.0921 54.048 29.6281 54.048 33.6601C54.048 37.5641 54.8 41.0201 56.304 44.0281C57.84 47.0041 60.016 49.3401 62.832 51.0361C65.648 52.7321 68.992 53.5801 72.864 53.5801C76.896 53.5801 80.384 52.6361 83.328 50.7481C86.304 48.8281 88.512 46.2201 89.952 42.9241L96.48 45.3721C92.7576 48.5 86.1164 49.9781 87.408 56.0761C83.28 58.6681 78.448 59.9641 72.912 59.9641ZM51.408 35.7721V30.4441H95.28L97.296 35.7721H51.408Z" fill="#F8E4D5"/>
<path d="M105.748 94.5C102.324 94.5 99.4284 93.988 97.0604 92.964C94.7244 91.94 92.8684 90.58 91.4924 88.884C90.1164 87.156 89.2204 85.236 88.8044 83.124H87.0764L88.4684 77.46C89.0764 80.82 90.7404 83.556 93.4604 85.668C96.2124 87.78 99.7804 88.836 104.164 88.836C107.908 88.836 110.804 88.084 112.852 86.58C114.932 85.044 115.972 82.82 115.972 79.908C115.972 77.252 114.948 75.188 112.9 73.716C110.852 72.244 107.268 71.124 102.148 70.356C94.8204 69.204 89.6044 67.396 86.5004 64.932C83.3964 62.436 81.8444 59.156 81.8444 55.092C81.8444 50.996 83.2684 47.716 86.1164 45.252C88.9644 42.788 92.9324 41.556 98.0204 41.556C101.38 41.556 104.148 42.068 106.324 43.092C108.532 44.116 110.228 45.492 111.412 47.22C112.596 48.916 113.364 50.788 113.716 52.836H115.444L114.052 58.26C113.476 54.996 111.908 52.34 109.348 50.292C106.788 48.212 103.444 47.172 99.3164 47.172C95.9884 47.172 93.3804 47.876 91.4924 49.284C89.6364 50.692 88.7084 52.564 88.7084 54.9C88.7084 57.364 89.8764 59.3 92.2124 60.708C94.5484 62.084 98.2284 63.172 103.252 63.972C110.356 65.092 115.396 66.932 118.372 69.492C121.348 72.052 122.836 75.508 122.836 79.86C122.836 84.436 121.364 88.02 118.42 90.612C115.508 93.204 111.284 94.5 105.748 94.5ZM81.5564 93.588V77.46H88.4684V93.588H81.5564ZM114.052 58.26V42.468H120.964L118.372 50.16L120.964 58.26H114.052Z" fill="#F8E4D5"/>
<path d="M157.3 75.84V56.736L157.588 52.992V46.896L157.3 40.176V5.952H143.524V0H164.212V70.272H173.99L173.9 75.5L157.3 75.84ZM138.052 23.76C142.276 23.76 146.052 24.688 149.38 26.544C152.74 28.4 155.508 31.056 157.684 34.512C159.86 37.936 161.3 42.048 162.004 46.848L157.588 50.16C157.588 46.224 156.82 42.736 155.284 39.696C153.78 36.656 151.62 34.288 148.804 32.592C145.988 30.864 142.644 30 138.772 30C134.964 30 131.652 30.864 128.836 32.592C126.02 34.32 123.844 36.704 122.308 39.744C120.804 42.784 120.052 46.304 120.052 50.304C120.052 54.272 120.836 57.776 122.404 60.816C123.972 63.856 126.164 66.256 128.98 68.016C131.828 69.744 135.092 70.608 138.772 70.608C142.612 70.608 145.924 69.744 148.708 68.016C151.524 66.256 153.7 63.84 155.236 60.768C156.804 57.696 157.588 54.16 157.588 50.16L158.644 61.056H157.012C156.436 63.872 155.316 66.48 153.652 68.88C151.988 71.248 149.796 73.168 147.076 74.64C144.356 76.08 141.092 76.8 137.284 76.8C133.732 76.8 130.468 76.16 127.492 74.88C124.516 73.568 121.94 71.728 119.764 69.36C117.588 66.96 115.892 64.144 114.676 60.912C113.492 57.68 112.9 54.144 112.9 50.304C112.9 45.12 113.94 40.528 116.02 36.528C118.1 32.528 121.012 29.408 124.756 27.168C128.532 24.896 132.964 23.76 138.052 23.76Z" fill="#F8E4D5"/>
<path d="M190.084 99.792C184.804 99.792 180.228 98.672 176.356 96.432C172.484 94.192 169.492 91.104 167.38 87.168C165.268 83.2 164.212 78.64 164.212 73.488C164.212 68.272 165.268 63.664 167.38 59.664C169.524 55.664 172.5 52.528 176.308 50.256C180.148 47.984 184.612 46.848 189.7 46.848C194.82 46.848 199.252 47.968 202.996 50.208C206.74 52.448 209.62 55.552 211.636 59.52C213.652 63.488 214.66 68.064 214.66 73.248C214.66 73.856 214.644 74.336 214.612 74.688C214.612 75.04 214.58 75.344 214.516 75.6H207.7C207.732 75.216 207.748 74.8 207.748 74.352C207.78 73.872 207.796 73.328 207.796 72.72C207.796 68.912 207.06 65.536 205.588 62.592C204.148 59.616 202.084 57.28 199.396 55.584C196.708 53.888 193.476 53.04 189.7 53.04C186.02 53.04 182.788 53.92 180.004 55.68C177.22 57.408 175.06 59.808 173.524 62.88C171.988 65.92 171.22 69.456 171.22 73.488C171.22 77.392 171.972 80.848 173.476 83.856C175.012 86.832 177.188 89.168 180.004 90.864C182.82 92.56 186.164 93.408 190.036 93.408C194.068 93.408 197.556 92.464 200.5 90.576C203.476 88.656 205.684 86.048 207.124 82.752L213.652 85.2C211.764 89.712 208.74 93.28 204.58 95.904C200.452 98.496 195.62 99.792 190.084 99.792ZM168.58 75.6V70.272H212.452L214.468 75.6H168.58Z" fill="#F8E4D5"/>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

@ -0,0 +1,39 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2320 5110 c-406 -39 -821 -184 -1154 -404 -642 -422 -1046 -1069
-1148 -1836 -16 -123 -16 -509 0 -625 65 -459 233 -873 501 -1230 92 -122 304
-341 426 -440 368 -299 822 -493 1305 -557 123 -16 509 -16 625 0 459 65 873
233 1230 501 122 92 341 304 440 426 299 368 493 822 557 1305 16 123 16 509
0 625 -69 486 -254 922 -551 1294 -406 507 -1012 845 -1671 931 -115 15 -443
21 -560 10z m585 -255 c756 -118 1400 -589 1735 -1270 108 -219 173 -428 217
-690 25 -154 25 -513 -1 -675 -109 -693 -514 -1296 -1112 -1654 -252 -150
-534 -251 -849 -303 -154 -25 -513 -25 -675 1 -419 66 -824 247 -1135 508
-452 379 -724 859 -822 1453 -25 154 -25 513 1 675 119 762 588 1404 1271
1740 256 126 526 202 830 234 85 9 444 -4 540 -19z"/>
<path d="M3570 4144 c-219 -47 -388 -224 -434 -452 -8 -41 -17 -76 -19 -79 -3
-2 -19 3 -35 11 -84 43 -205 68 -353 73 -161 6 -264 -9 -369 -51 -210 -86
-384 -285 -445 -511 -10 -36 -22 -62 -26 -59 -5 3 -9 135 -9 295 l0 289 -340
0 -340 0 0 -115 0 -115 205 0 205 0 0 -1190 0 -1190 -205 0 -205 0 0 -115 0
-115 545 0 545 0 0 115 0 115 -202 2 -203 3 -3 463 c-1 254 1 462 5 462 5 0
19 -17 33 -37 38 -56 144 -158 212 -204 223 -152 528 -196 819 -120 256 68
471 244 593 488 90 180 121 339 113 583 -3 91 -10 181 -15 200 l-9 35 101 1
c174 2 302 50 414 158 59 56 122 145 122 172 0 15 -144 68 -148 54 -2 -5 -23
-37 -47 -69 -83 -113 -191 -164 -345 -165 -136 -1 -139 0 -184 91 -53 107
-100 176 -175 255 l-63 68 497 0 498 0 -6 98 c-8 118 -25 184 -73 281 -109
221 -379 335 -654 275z m285 -149 c69 -20 152 -80 197 -144 37 -53 74 -149 80
-206 l3 -30 -422 -3 c-485 -3 -438 -14 -403 101 72 233 299 351 545 282z
m-1035 -560 c36 -9 103 -34 150 -57 117 -58 247 -186 309 -306 109 -211 128
-494 51 -735 -40 -125 -90 -207 -185 -302 -63 -64 -98 -90 -165 -123 -141 -69
-301 -94 -461 -73 -311 41 -527 233 -611 543 -20 73 -23 106 -22 263 0 167 2
187 28 270 44 143 98 238 191 330 90 90 160 135 266 171 134 45 304 52 449 19z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,19 @@
{
"name": "pesde",
"short_name": "pesde",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#f8e4d5",
"background_color": "#13100F",
"display": "standalone"
}

13
website/svelte.config.js Normal file
View file

@ -0,0 +1,13 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter()
}
};
export default config;

View file

@ -0,0 +1,40 @@
import defaultTheme from 'tailwindcss/defaultTheme';
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {
colors: {
'standard-text': '#f8e4d5',
'main-background': '#13100F',
'paper-1': '#422911',
'paper-1-alt': '#4C3C2D',
links: '#ffa360'
},
fontFamily: {
serif: ['Hepta Slab Variable', defaultTheme.fontFamily.serif]
},
typography: ({ theme }) => ({
pesde: {
css: {
'--tw-prose-body': theme('colors.standard-text'),
'--tw-prose-headings': theme('colors.standard-text'),
'--tw-prose-lead': theme('colors.orange[100]'),
'--tw-prose-links': theme('colors.links'),
'--tw-prose-bold': theme('colors.orange[400]'),
'--tw-prose-counters': theme('colors.orange[300]'),
'--tw-prose-bullets': theme('colors.orange[300]'),
'--tw-prose-hr': theme('colors.orange[100]'),
'--tw-prose-quotes': theme('colors.orange[300]'),
'--tw-prose-quote-borders': theme('colors.orange[500]'),
'--tw-prose-captions': theme('colors.orange[300]'),
'--tw-prose-th-borders': theme('colors.orange[300]'),
'--tw-prose-td-borders': theme('colors.orange[300]')
}
}
})
}
},
plugins: [require('@tailwindcss/typography')]
};

14
website/tsconfig.json Normal file
View file

@ -0,0 +1,14 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
}

6
website/vite.config.ts Normal file
View file

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