diff --git a/registry/src/endpoints/publish_version.rs b/registry/src/endpoints/publish_version.rs index d6a38ab..043f5ff 100644 --- a/registry/src/endpoints/publish_version.rs +++ b/registry/src/endpoints/publish_version.rs @@ -61,7 +61,7 @@ const ADDITIONAL_FORBIDDEN_FILES: &[&str] = &["default.project.json"]; struct DocEntryInfo { #[serde(default)] label: Option, - #[serde(default)] + #[serde(default, alias = "position")] sidebar_position: Option, #[serde(default)] collapsed: bool, diff --git a/website/bun.lockb b/website/bun.lockb index 0d891c3..db992e7 100755 Binary files a/website/bun.lockb and b/website/bun.lockb differ diff --git a/website/package.json b/website/package.json index 6f41d22..9a26eeb 100644 --- a/website/package.json +++ b/website/package.json @@ -12,47 +12,57 @@ "format": "prettier --write ." }, "devDependencies": { - "@sveltejs/adapter-vercel": "^5.4.5", - "@sveltejs/kit": "^2.7.0", - "@sveltejs/vite-plugin-svelte": "^4.0.0-next.8", + "@sveltejs/adapter-vercel": "^5.4.6", + "@sveltejs/kit": "^2.7.3", + "@sveltejs/vite-plugin-svelte": "^4.0.0", "@tailwindcss/typography": "^0.5.15", "@types/eslint": "^9.6.1", "@types/gunzip-maybe": "^1.4.2", "@types/tar-stream": "^3.1.3", "autoprefixer": "^10.4.20", - "eslint": "^9.12.0", + "eslint": "^9.13.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.44.1", + "eslint-plugin-svelte": "^2.46.0", "globals": "^15.11.0", "mdsvex": "^0.12.3", "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.2.7", "prettier-plugin-tailwindcss": "^0.6.8", - "svelte": "^5.0.0-next.264", + "svelte": "^5.1.4", "svelte-check": "^4.0.5", - "tailwindcss": "^3.4.13", + "tailwindcss": "^3.4.14", "typescript": "^5.6.3", - "typescript-eslint": "^8.8.1", - "vite": "^5.4.8" + "typescript-eslint": "^8.12.2", + "vite": "^5.4.10" }, "type": "module", "dependencies": { "@fontsource-variable/nunito-sans": "^5.1.0", - "@shikijs/rehype": "^1.22.0", - "bits-ui": "^0.21.16", + "@shikijs/rehype": "^1.22.2", + "@types/hast": "^3.0.4", + "@types/unist": "^3.0.3", + "bits-ui": "next", "date-fns": "^4.1.0", "gunzip-maybe": "^1.4.2", + "hast-util-heading": "^3.0.0", + "hast-util-heading-rank": "^3.0.0", + "hast-util-to-text": "^4.0.2", "lucide-svelte": "^0.446.0", + "rehype-infer-description-meta": "^2.0.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", + "rehype-slug": "^6.0.0", "rehype-stringify": "^10.0.1", + "remark-frontmatter": "^5.0.0", "remark-gemoji": "^8.0.0", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.1", - "shiki": "^1.22.0", + "shiki": "^1.22.2", "tar-stream": "^3.1.7", - "unified": "^11.0.5" + "unified": "^11.0.5", + "unist-util-map": "^4.0.0", + "vfile": "^6.0.3" }, "patchedDependencies": { "@shikijs/rehype@1.22.0": "patches/@shikijs%2Frehype@1.22.0.patch" diff --git a/website/src/app.css b/website/src/app.css index 660b89e..0883f6a 100644 --- a/website/src/app.css +++ b/website/src/app.css @@ -7,6 +7,7 @@ --color-card: 245 230 210; --color-card-hover: 240 225 205; --color-border: 200 180 160; + --color-header: 250 234 215; --color-body: 84 70 50; --color-heading: 70 55 35; @@ -44,13 +45,14 @@ --color-card: 28 22 17; --color-card-hover: 40 32 25; --color-border: 28 22 17; + --color-header: 20 16 12; --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-input-bg: 20 13 8; + --color-input-border: 78 60 40; --color-placeholder: 169 147 128; --color-primary: 241 157 30; @@ -61,7 +63,8 @@ } html { - scroll-padding-top: theme(spacing.16); + scroll-padding-top: theme(spacing.24); + color-scheme: light dark; } body { @@ -78,3 +81,12 @@ body { opacity: 0; } } + +.hide-scrollbar::-webkit-scrollbar { + display: none; +} + +.hide-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; +} diff --git a/website/src/lib/components/Logomark.svelte b/website/src/lib/components/Logomark.svelte new file mode 100644 index 0000000..21e8346 --- /dev/null +++ b/website/src/lib/components/Logomark.svelte @@ -0,0 +1,9 @@ + + pesde + + diff --git a/website/src/lib/components/Select.svelte b/website/src/lib/components/Select.svelte new file mode 100644 index 0000000..f593bff --- /dev/null +++ b/website/src/lib/components/Select.svelte @@ -0,0 +1,74 @@ + + + + + {#snippet child({ props })} + {#if trigger} + {@render trigger(props, triggerLabel)} + {:else} + + {/if} + {/snippet} + + + + {#each items as { value, label, disabled } (value)} + + {#snippet children({ selected })} + {label} + {#if selected} + + {/if} + {/snippet} + + {/each} + + + diff --git a/website/src/lib/markdown.ts b/website/src/lib/markdown.ts new file mode 100644 index 0000000..2cab9b2 --- /dev/null +++ b/website/src/lib/markdown.ts @@ -0,0 +1,116 @@ +import rehypeShikiFromHighlighter from "@shikijs/rehype/core" +import type { Nodes } from "hast" +import { heading } from "hast-util-heading" +import { headingRank } from "hast-util-heading-rank" +import { toText } from "hast-util-to-text" +import rehypeInferDescriptionMeta from "rehype-infer-description-meta" +import rehypeRaw from "rehype-raw" +import rehypeSanitize from "rehype-sanitize" +import rehypeSlug from "rehype-slug" +import rehypeStringify from "rehype-stringify" +import remarkFrontmatter from "remark-frontmatter" +import remarkGemoji from "remark-gemoji" +import remarkGfm from "remark-gfm" +import remarkParse from "remark-parse" +import remarkRehype from "remark-rehype" +import { createCssVariablesTheme, createHighlighter } from "shiki" +import { unified } from "unified" +import type { Node } from "unist" +import { map } from "unist-util-map" + +const highlighter = createHighlighter({ + themes: [], + langs: [], +}) + +export const markdown = (async () => { + return unified() + .use(remarkParse) + .use(remarkFrontmatter) + .use(remarkGfm) + .use(remarkGemoji) + .use(remarkRehype, { allowDangerousHtml: true }) + .use(rehypeRaw) + .use(rehypeSanitize) + .use(rehypeShikiFromHighlighter, await highlighter, { + lazy: true, + theme: createCssVariablesTheme({ + name: "css-variables", + variablePrefix: "--shiki-", + variableDefaults: {}, + fontStyle: true, + }), + fallbackLanguage: "text", + }) + .use(rehypeStringify) + .freeze() +})() + +export type TocItem = { + id: string + title: string + level: number +} + +export const docsMarkdown = (async () => { + return unified() + .use(remarkParse) + .use(remarkFrontmatter) + .use(remarkGfm) + .use(remarkGemoji) + .use(remarkRehype, { allowDangerousHtml: true, clobberPrefix: "" }) + .use(rehypeSlug) + .use(() => (node, file) => { + const toc: TocItem[] = [] + file.data.toc = toc + + return map(node as Nodes, (node) => { + if (node.type === "element" && node.tagName === "a") { + const fullUrl = new URL(node.properties.href as string, `file://${file.path}`) + + let href = node.properties.href as string + if (fullUrl.protocol === "file:") { + href = file.data.basePath + fullUrl.pathname.replace(/\.mdx?$/, "") + fullUrl.hash + } + + return { + ...node, + properties: { + ...node.properties, + href, + }, + } + } + + if (heading(node)) { + const rank = headingRank(node) + if (rank && typeof node.properties.id === "string" && rank >= 2 && rank <= 3) { + toc.push({ + id: node.properties.id, + title: toText(node), + level: rank, + }) + } + } + + return node + }) as Node + }) + .use(rehypeRaw) + .use(rehypeSanitize) + .use(rehypeShikiFromHighlighter, await highlighter, { + lazy: true, + theme: createCssVariablesTheme({ + name: "css-variables", + variablePrefix: "--shiki-", + variableDefaults: {}, + fontStyle: true, + }), + fallbackLanguage: "text", + }) + .use(rehypeInferDescriptionMeta, { + selector: "p", + }) + .use(rehypeStringify) + .freeze() +})() diff --git a/website/src/lib/registry-api.ts b/website/src/lib/registry-api.ts index 06a3c62..99900ae 100644 --- a/website/src/lib/registry-api.ts +++ b/website/src/lib/registry-api.ts @@ -19,6 +19,7 @@ export type PackageResponse = { authors?: string[] repository?: string dependencies: Record + docs?: DocEntry[] } export type TargetInfo = { @@ -31,19 +32,38 @@ export type TargetKind = "roblox" | "roblox_server" | "lune" | "luau" export type DependencyEntry = [DependencyInfo, DependencyKind] -export type DependencyInfo = { - index: string - name: string - target?: string - version: string -} | { - index: string, - wally: string, - version: string, -} +export type DependencyInfo = + | { + index: string + name: string + target?: string + version: string + } + | { + index: string + wally: string + version: string + } export type DependencyKind = "standard" | "peer" | "dev" +export type DocEntry = DocEntryCategory | DocEntryPage + +export type DocEntryBase = { + label: string + position: number +} + +export type DocEntryCategory = DocEntryBase & { + items?: DocEntry[] + collapsed?: boolean +} + +export type DocEntryPage = DocEntryBase & { + name: string + hash: string +} + export const TARGET_KIND_DISPLAY_NAMES: Record = { roblox: "Roblox", roblox_server: "Roblox (server)", diff --git a/website/src/routes/(app)/+error.svelte b/website/src/routes/(app)/+error.svelte new file mode 100644 index 0000000..99b3ae6 --- /dev/null +++ b/website/src/routes/(app)/+error.svelte @@ -0,0 +1,8 @@ + + +
+

{$page.status}

+

{$page.error?.message}

+
diff --git a/website/src/routes/(app)/+layout.svelte b/website/src/routes/(app)/+layout.svelte new file mode 100644 index 0000000..29c6736 --- /dev/null +++ b/website/src/routes/(app)/+layout.svelte @@ -0,0 +1,14 @@ + + +
+ +
+ {@render children()} +
+ +