diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index 4874a982200c..fee631a6c57a 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -113,6 +113,24 @@ jobs:
package: ${{ steps.extract-tag.outputs.package }}
version: ${{ steps.extract-tag.outputs.semver }}
+ - name: Upload split documentation to blob storage
+ if: ${{ env.REF_TYPE == 'tag' && (!inputs.ref || inputs.ref == 'main') }}
+ env:
+ BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
+ uses: ./packages/actions/src/uploadSplitDocumentation
+ with:
+ package: ${{ steps.extract-tag.outputs.package }}
+ version: ${{ steps.extract-tag.outputs.semver }}
+
+ - name: Upload split documentation to blob storage
+ if: ${{ env.REF_TYPE == 'tag' && inputs.ref && inputs.ref != 'main' }}
+ env:
+ BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
+ uses: ./main/packages/actions/src/uploadSplitDocumentation
+ with:
+ package: ${{ steps.extract-tag.outputs.package }}
+ version: ${{ steps.extract-tag.outputs.semver }}
+
- name: Move docs to correct directory
if: ${{ env.REF_TYPE == 'tag' }}
env:
@@ -141,6 +159,18 @@ jobs:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
uses: ./main/packages/actions/src/uploadDocumentation
+ - name: Upload split documentation to blob storage
+ if: ${{ env.REF_TYPE == 'branch' && (!inputs.ref || inputs.ref == 'main') }}
+ env:
+ BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
+ uses: ./packages/actions/src/uploadSplitDocumentation
+
+ - name: Upload split documentation to blob storage
+ if: ${{ env.REF_TYPE == 'branch' && inputs.ref && inputs.ref != 'main' }}
+ env:
+ BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
+ uses: ./main/packages/actions/src/uploadSplitDocumentation
+
- name: Move docs to correct directory
if: ${{ env.REF_TYPE == 'branch' }}
run: |
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 192cc66f4ead..c29baf2a5b47 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -8,7 +8,6 @@
"eamodio.gitlens",
"christian-kohler.npm-intellisense",
"christian-kohler.path-intellisense",
- "antfu.unocss",
"unifiedjs.vscode-mdx"
]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7f5067db8d03..7f3769bc0fc6 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -26,6 +26,7 @@
"npm.packageManager": "pnpm",
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
+ "unocss.disable": true,
"deno.enable": false,
"deno.enablePaths": ["./packages/create-discord-bot/template/Deno"],
"deno.lint": false,
diff --git a/apps/guide/package.json b/apps/guide/package.json
index ad2f82bd0d8c..c58725a211fa 100644
--- a/apps/guide/package.json
+++ b/apps/guide/package.json
@@ -47,8 +47,8 @@
"@code-hike/mdx": "^0.9.0",
"@discordjs/ui": "workspace:^",
"@react-icons/all-files": "^4.1.0",
- "@vercel/analytics": "^1.1.3",
- "@vercel/edge-config": "^0.4.1",
+ "@vercel/analytics": "^1.2.2",
+ "@vercel/edge-config": "^1.1.0",
"@vercel/og": "^0.6.2",
"ariakit": "2.0.0-next.44",
"cmdk": "^0.2.1",
@@ -70,28 +70,28 @@
"@testing-library/user-event": "^14.5.2",
"@types/html-escaper": "^3.0.2",
"@types/node": "18.18.8",
- "@types/react": "^18.2.54",
- "@types/react-dom": "^18.2.18",
+ "@types/react": "^18.2.60",
+ "@types/react-dom": "^18.2.19",
"@unocss/eslint-plugin": "^0.58.5",
"@unocss/postcss": "^0.58.5",
"@unocss/reset": "^0.58.5",
"@vitejs/plugin-react": "^4.2.1",
- "@vitest/coverage-v8": "^1.2.2",
+ "@vitest/coverage-v8": "^1.3.1",
"cross-env": "^7.0.3",
- "eslint": "^8.56.0",
- "eslint-config-neon": "^0.1.58",
+ "eslint": "^8.57.0",
+ "eslint-config-neon": "^0.1.59",
"eslint-formatter-pretty": "^6.0.1",
- "happy-dom": "^13.3.8",
+ "happy-dom": "^13.6.2",
"hast-util-to-string": "^2.0.0",
"hastscript": "^8.0.0",
"html-escaper": "^3.0.3",
- "postcss": "^8.4.34",
+ "postcss": "^8.4.35",
"prettier": "^3.2.5",
- "turbo": "^1.12.2",
+ "turbo": "^1.12.4",
"typescript": "^5.3.3",
"unocss": "^0.58.5",
- "vercel": "^33.4.1",
- "vitest": "^1.2.2"
+ "vercel": "^33.5.3",
+ "vitest": "^1.3.1"
},
"engines": {
"node": ">=18"
diff --git a/apps/website/.env.development b/apps/website/.env.development
index 305279a9d052..0333ed8215fc 100644
--- a/apps/website/.env.development
+++ b/apps/website/.env.development
@@ -1,2 +1 @@
NEXT_PUBLIC_LOCAL_DEV=true
-METADATA_BASE_URL=http://localhost:3000
diff --git a/apps/website/.gitignore b/apps/website/.gitignore
index 630f0f3c34cf..529c998d82d5 100644
--- a/apps/website/.gitignore
+++ b/apps/website/.gitignore
@@ -28,3 +28,5 @@ src/styles/unocss.css
lighthouse-results
.vercel
+
+old_src
diff --git a/apps/website/.prettierrc.cjs b/apps/website/.prettierrc.cjs
index f723230a05d4..f1c65b39d161 100644
--- a/apps/website/.prettierrc.cjs
+++ b/apps/website/.prettierrc.cjs
@@ -1,2 +1,5 @@
/** @type {import('prettier').Config} */
-module.exports = require('../../.prettierrc.json');
+module.exports = {
+ ...require('../../.prettierrc.json'),
+ plugins: ['prettier-plugin-tailwindcss'],
+};
diff --git a/apps/website/next.config.mjs b/apps/website/next.config.mjs
index a6fe69f2198f..a58114a0b386 100644
--- a/apps/website/next.config.mjs
+++ b/apps/website/next.config.mjs
@@ -1,4 +1,5 @@
import bundleAnalyzer from '@next/bundle-analyzer';
+import localesPlugin from '@react-aria/optimize-locales-plugin';
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
@@ -6,18 +7,26 @@ const withBundleAnalyzer = bundleAnalyzer({
export default withBundleAnalyzer({
reactStrictMode: true,
- experimental: {
- typedRoutes: true,
- serverComponentsExternalPackages: ['@rushstack/node-core-library', '@discordjs/api-extractor-model', 'jju'],
- },
images: {
dangerouslyAllowSVG: true,
contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; frame-src 'none'; sandbox;",
},
- poweredByHeader: false,
- env: {
- MAX_FETCH_SIZE: '5',
+ logging: {
+ fetches: {
+ fullUrl: true,
+ },
+ },
+ experimental: {
+ ppr: false,
+ },
+ webpack(config, { isServer }) {
+ if (!isServer) {
+ // Don't include any locale strings in the client JS bundle.
+ config.plugins.push(localesPlugin.webpack({ locales: [] }));
+ }
+
+ return config;
},
async redirects() {
return [
diff --git a/apps/website/package.json b/apps/website/package.json
index 1b9c2cb074ce..3088c38bae70 100644
--- a/apps/website/package.json
+++ b/apps/website/package.json
@@ -46,58 +46,60 @@
},
"homepage": "https://discord.js.org",
"dependencies": {
- "@discordjs/api-extractor-model": "workspace:^",
- "@discordjs/api-extractor-utils": "workspace:^",
- "@discordjs/scripts": "workspace:^",
- "@discordjs/ui": "workspace:^",
- "@microsoft/tsdoc": "^0.14.2",
- "@microsoft/tsdoc-config": "0.16.2",
+ "@radix-ui/react-collapsible": "^1.0.3",
"@react-icons/all-files": "^4.1.0",
- "@vercel/analytics": "^1.1.3",
- "@vercel/edge-config": "^0.4.1",
+ "@vercel/analytics": "^1.2.2",
+ "@vercel/blob": "^0.22.1",
+ "@vercel/edge-config": "^1.1.0",
"@vercel/og": "^0.6.2",
"@vercel/postgres": "^0.7.2",
- "ariakit": "2.0.0-next.44",
- "bright": "^0.8.4",
- "class-variance-authority": "^0.7.0",
"cmdk": "^0.2.1",
+ "geist": "^1.2.2",
+ "jotai": "^2.7.0",
+ "lucide-react": "^0.343.0",
"meilisearch": "^0.37.0",
- "next": "14.1.0",
+ "next": "14.1.1-canary.80",
"next-mdx-remote": "^4.4.1",
"next-themes": "^0.2.1",
+ "overlayscrollbars": "^2.5.0",
+ "overlayscrollbars-react": "^0.5.4",
"react": "^18.2.0",
- "react-custom-scrollbars-2": "^4.5.0",
+ "react-aria-components": "^1.1.1",
"react-dom": "^18.2.0",
- "react-use": "^17.5.0",
- "rehype-slug": "^5.1.0",
- "remark-gfm": "^3.0.1",
"sharp": "^0.33.2",
- "swr": "^2.2.4"
+ "usehooks-ts": "^2.15.1",
+ "vaul": "^0.9.0"
},
"devDependencies": {
- "@next/bundle-analyzer": "14.1.0",
+ "@next/bundle-analyzer": "14.1.1-canary.80",
+ "@react-aria/optimize-locales-plugin": "^1.0.2",
+ "@shikijs/rehype": "1.1.7",
+ "@tailwindcss/typography": "^0.5.10",
"@testing-library/react": "^14.2.1",
"@testing-library/user-event": "^14.5.2",
"@types/node": "18.18.8",
- "@types/react": "^18.2.54",
- "@types/react-dom": "^18.2.18",
- "@unocss/eslint-plugin": "^0.58.5",
- "@unocss/postcss": "^0.58.5",
- "@unocss/reset": "^0.58.5",
+ "@types/react": "^18.2.60",
+ "@types/react-dom": "^18.2.19",
"@vitejs/plugin-react": "^4.2.1",
- "@vitest/coverage-v8": "^1.2.2",
+ "@vitest/coverage-v8": "^1.3.1",
+ "autoprefixer": "^10.4.17",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
- "eslint": "^8.56.0",
- "eslint-config-neon": "^0.1.58",
+ "eslint": "^8.57.0",
+ "eslint-config-neon": "^0.1.59",
"eslint-formatter-pretty": "^6.0.1",
- "happy-dom": "^13.3.8",
- "postcss": "^8.4.34",
+ "happy-dom": "^13.6.2",
+ "postcss": "^8.4.35",
"prettier": "^3.2.5",
- "turbo": "^1.12.2",
+ "prettier-plugin-tailwindcss": "^0.5.11",
+ "remark-gfm": "^3.0.1",
+ "remark-rehype": "^11.1.0",
+ "shiki": "1.1.7",
+ "tailwindcss": "^3.4.1",
+ "turbo": "^1.12.4",
"typescript": "^5.3.3",
- "vercel": "^33.4.1",
- "vitest": "^1.2.2"
+ "vercel": "^33.5.3",
+ "vitest": "^1.3.1"
},
"engines": {
"node": ">=18"
diff --git a/apps/website/postcss.config.cjs b/apps/website/postcss.config.cjs
index 5d9116500fed..e873f1a4f235 100644
--- a/apps/website/postcss.config.cjs
+++ b/apps/website/postcss.config.cjs
@@ -1,5 +1,6 @@
module.exports = {
plugins: {
- '@unocss/postcss': {},
+ tailwindcss: {},
+ autoprefixer: {},
},
};
diff --git a/apps/website/src/app/_global-error.tsx b/apps/website/src/app/_global-error.tsx
deleted file mode 100644
index a1f9bc5c728b..000000000000
--- a/apps/website/src/app/_global-error.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-'use client';
-
-import { Analytics } from '@vercel/analytics/react';
-import { inter } from '~/util/fonts';
-import { Providers } from './providers';
-
-import '~/styles/cmdk.css';
-import '~/styles/main.css';
-
-export default function GlobalError({ error }: { readonly error: Error }) {
- console.error(error);
-
- return (
-
-
-
-
-
-
500
- Error.
-
-
-
-
-
-
- );
-}
diff --git a/apps/website/src/app/api/[package]/versions/route.ts b/apps/website/src/app/api/[package]/versions/route.ts
deleted file mode 100644
index 53dc59cac4c7..000000000000
--- a/apps/website/src/app/api/[package]/versions/route.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import type { NextRequest } from 'next/server';
-import { NextResponse } from 'next/server';
-import { fetchVersions } from '~/app/docAPI';
-
-export async function GET(req: NextRequest) {
- const packageName = req.nextUrl.pathname.split('/').slice(2, 3)[0] ?? 'discord.js';
- return NextResponse.json(await fetchVersions(packageName));
-}
diff --git a/apps/website/src/app/api/dynamic-open-graph.png/route.tsx b/apps/website/src/app/api/dynamic-open-graph.png/route.tsx
deleted file mode 100644
index 5b9208f44798..000000000000
--- a/apps/website/src/app/api/dynamic-open-graph.png/route.tsx
+++ /dev/null
@@ -1,170 +0,0 @@
-/* eslint-disable react/no-unknown-property */
-
-import type { ApiItemKind } from '@discordjs/api-extractor-model';
-import { ImageResponse } from '@vercel/og';
-import type { NextRequest } from 'next/server';
-import { resolvePackageName } from '~/util/resolvePackageName';
-
-export const runtime = 'edge';
-
-const fonts = Promise.all([
- fetch(new URL('../../../assets/fonts/Inter-Regular.ttf', import.meta.url)).then(async (res) => res.arrayBuffer()),
- fetch(new URL('../../../assets/fonts/Inter-Bold.ttf', import.meta.url)).then(async (res) => res.arrayBuffer()),
-]);
-
-function resolveIcon(icon: keyof typeof ApiItemKind, size = 88) {
- switch (icon) {
- case 'Class':
- return (
-
- );
- case 'Enum':
- return (
-
- );
- case 'EnumMember':
- return (
-
- );
- case 'Interface':
- return (
-
- );
- case 'TypeAlias':
- return (
-
- );
- case 'Variable':
- return (
-
- );
- case 'Property':
- return (
-
- );
- default:
- return (
-
- );
- }
-}
-
-export async function GET(request: NextRequest) {
- const fontData = await fonts;
-
- const { searchParams } = new URL(request.url);
-
- const hasPkg = searchParams.has('pkg');
- const hasKind = searchParams.has('kind');
- const hasName = searchParams.has('name');
- const hasMethods = searchParams.has('methods');
- const hasProps = searchParams.has('props');
- const hasMembers = searchParams.has('members');
- const pkg = hasPkg ? resolvePackageName(searchParams.get('pkg')!) : '';
- const kind = hasKind ? searchParams.get('kind')! : 'Method';
- const name = hasName ? searchParams.get('name')!.slice(0, 100) : 'My default name which is super long to overflow';
- const methods = hasMethods ? searchParams.get('methods') : '';
- const props = hasProps ? searchParams.get('props') : '';
- const members = hasMembers ? searchParams.get('members') : '';
-
- return new ImageResponse(
- (
-
-
-
{pkg}
-
-
- {resolveIcon(kind as keyof typeof ApiItemKind)}
-
- {name}
-
-
-
-
- {props ? (
-
-
{resolveIcon('Property', 36)}
-
- {props}
- Properties
-
-
- ) : null}
- {methods ? (
-
-
{resolveIcon('Method', 36)}
-
- {methods}
- Methods
-
-
- ) : null}
- {members ? (
-
-
{resolveIcon('EnumMember', 36)}
-
- {members}
- Members
-
-
- ) : null}
-
-
- discord.js
-
-
-
-
-
- ),
- {
- width: 1_200,
- height: 630,
- fonts: [
- { name: 'Inter', data: fontData[0], weight: 500, style: 'normal' },
- { name: 'Inter', data: fontData[1], weight: 700, style: 'normal' },
- ],
- debug: false,
- },
- );
-}
diff --git a/apps/website/src/app/api/open-graph.png/route.tsx b/apps/website/src/app/api/open-graph.png/route.tsx
deleted file mode 100644
index 9ebc85d4fd62..000000000000
--- a/apps/website/src/app/api/open-graph.png/route.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/* eslint-disable react/no-unknown-property */
-
-import { ImageResponse } from '@vercel/og';
-
-export const runtime = 'edge';
-
-const fonts = fetch(new URL('../../../assets/fonts/Inter-Black.ttf', import.meta.url)).then(async (res) =>
- res.arrayBuffer(),
-);
-
-export async function GET() {
- const fontData = await fonts;
-
- return new ImageResponse(
- (
-
-
-
-
-
-
- The most popular
-
-
way to build Discord
-
bots.
-
-
-
-
-
- ),
- {
- width: 1_200,
- height: 630,
- fonts: [{ name: 'Inter', data: fontData, weight: 900, style: 'normal' }],
- },
- );
-}
diff --git a/apps/website/src/app/docAPI.ts b/apps/website/src/app/docAPI.ts
deleted file mode 100644
index be08ff226764..000000000000
--- a/apps/website/src/app/docAPI.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { readFile } from 'node:fs/promises';
-import { join } from 'node:path';
-import { sql } from '@vercel/postgres';
-
-export const fetchVersions = async (packageName: string): Promise => {
- if (process.env.NEXT_PUBLIC_LOCAL_DEV === 'true' || process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') {
- return ['main'];
- }
-
- try {
- const { rows } = await sql`select version from documentation where name = ${packageName} order by version desc`;
-
- return rows.map((row) => row.version);
- } catch {
- return [];
- }
-};
-
-export const fetchModelJSON = async (packageName: string, version: string) => {
- if (process.env.NEXT_PUBLIC_LOCAL_DEV === 'true') {
- try {
- const res = await readFile(
- join(process.cwd(), '..', '..', 'packages', packageName, 'docs', 'docs.api.json'),
- 'utf8',
- );
-
- return JSON.parse(res);
- } catch {
- return null;
- }
- }
-
- if (process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') {
- try {
- const { rows } = await sql`select url from documentation where name = ${packageName} and version = ${'main'}`;
- const res = await fetch(rows[0]?.url ?? '');
-
- return await res.json();
- } catch {
- return null;
- }
- }
-
- try {
- const { rows } = await sql`select url from documentation where name = ${packageName} and version = ${version}`;
- const res = await fetch(rows[0]?.url ?? '');
-
- return await res.json();
- } catch {
- return null;
- }
-};
diff --git a/apps/website/src/app/docs/packages/[packageName]/[version]/[item]/opengraph-image.tsx b/apps/website/src/app/docs/packages/[packageName]/[version]/[item]/opengraph-image.tsx
new file mode 100644
index 000000000000..c8c16e1f4401
--- /dev/null
+++ b/apps/website/src/app/docs/packages/[packageName]/[version]/[item]/opengraph-image.tsx
@@ -0,0 +1,98 @@
+/* eslint-disable react/no-unknown-property */
+import { ImageResponse } from 'next/og';
+import { resolveKind } from '~/util/resolveNodeKind';
+
+export const runtime = 'edge';
+
+export const size = {
+ width: 1_200,
+ height: 630,
+};
+
+export const contentType = 'image/png';
+
+export default async function Image({
+ params,
+}: {
+ readonly params: { readonly item: string; readonly packageName: string; readonly version: string };
+}) {
+ const normalizeItem = params.item.split(encodeURIComponent(':')).join('.').toLowerCase();
+
+ const isMainVersion = params.version === 'main';
+ const fileContent = await fetch(
+ `${process.env.BLOB_STORAGE_URL}/rewrite/${params.packageName}/${params.version}.${normalizeItem}.api.json`,
+ { next: isMainVersion ? { revalidate: 0 } : { revalidate: 604_800 } },
+ );
+ const node = await fileContent.json();
+
+ return new ImageResponse(
+ (
+
+
+
{params.packageName}
+
+
+ {resolveKind(node.kind, 94)}
+
+ {node.displayName}
+
+
+
+
+ {node.members?.properties?.length ? (
+
+
{resolveKind('Property', 42)}
+
+ {node.members.properties.length}
+ Properties
+
+
+ ) : null}
+ {node.members?.events?.length ? (
+
+
{resolveKind('Method', 42)}
+
+ {node.members.events.length}
+ Events
+
+
+ ) : null}
+ {node.members?.methods?.length ? (
+
+
{resolveKind('Method', 42)}
+
+ {node.members.methods.length}
+ Methods
+
+
+ ) : null}
+ {node.members?.length ? (
+
+
{resolveKind('EnumMember', 42)}
+
+ {node.members.length}
+ Members
+
+
+ ) : null}
+
+
+ discord.js
+
+
+
+
+
+ ),
+ {
+ ...size,
+ },
+ );
+}
diff --git a/apps/website/src/app/docs/packages/[packageName]/[version]/[item]/page.tsx b/apps/website/src/app/docs/packages/[packageName]/[version]/[item]/page.tsx
new file mode 100644
index 000000000000..1999b0fe3cfe
--- /dev/null
+++ b/apps/website/src/app/docs/packages/[packageName]/[version]/[item]/page.tsx
@@ -0,0 +1,50 @@
+// import { readFile } from 'node:fs/promises';
+// import { join } from 'node:path';
+// import { inspect } from 'node:util';
+import type { Metadata } from 'next';
+import { DocItem } from '~/components/DocItem';
+
+export async function generateMetadata({
+ params,
+}: {
+ readonly params: {
+ readonly item: string;
+ readonly packageName: string;
+ readonly version: string;
+ };
+}): Promise {
+ const normalizeItem = params.item.split(encodeURIComponent(':'))[0];
+
+ return {
+ title: `${normalizeItem} (${params.packageName} - ${params.version})`,
+ };
+}
+
+export default async function Page({
+ params,
+}: {
+ readonly params: { readonly item: string; readonly packageName: string; readonly version: string };
+}) {
+ const normalizeItem = params.item.split(encodeURIComponent(':')).join('.').toLowerCase();
+
+ // const fileContent = await readFile(
+ // join(process.cwd(), `../../packages/${params.packageName}/docs/split/${params.version}.${normalizeItem}.api.json`),
+ // 'utf8',
+ // );
+ // const node = JSON.parse(fileContent);
+
+ const isMainVersion = params.version === 'main';
+ const fileContent = await fetch(
+ `${process.env.BLOB_STORAGE_URL}/rewrite/${params.packageName}/${params.version}.${normalizeItem}.api.json`,
+ { next: isMainVersion ? { revalidate: 0 } : { revalidate: 604_800 } },
+ );
+ const node = await fileContent.json();
+
+ // console.log(inspect(node, { depth: 0 }));
+
+ return (
+
+
+
+ );
+}
diff --git a/apps/website/src/app/docs/packages/[packageName]/[version]/layout.tsx b/apps/website/src/app/docs/packages/[packageName]/[version]/layout.tsx
new file mode 100644
index 000000000000..2f020d7549d9
--- /dev/null
+++ b/apps/website/src/app/docs/packages/[packageName]/[version]/layout.tsx
@@ -0,0 +1,87 @@
+// import { readFile } from 'node:fs/promises';
+// import { join } from 'node:path';
+// import { inspect } from 'node:util';
+import type { Metadata } from 'next';
+import dynamic from 'next/dynamic';
+import type { PropsWithChildren } from 'react';
+import { Navigation } from '~/components/Navigation';
+import { OverlayScrollbarsComponent } from '~/components/OverlayScrollbars';
+import { Drawer } from '~/components/ui/Drawer';
+import { Footer } from '~/components/ui/Footer';
+
+// eslint-disable-next-line promise/prefer-await-to-then
+const CmdK = dynamic(async () => import('~/components/ui/CmdK').then((mod) => mod.CmdK), { ssr: false });
+
+export async function generateMetadata({
+ params,
+}: {
+ readonly params: { readonly packageName: string; readonly version: string };
+}): Promise {
+ return {
+ title: {
+ template: '%s | discord.js',
+ default: `${params.packageName} (${params.version})`,
+ },
+ };
+}
+
+export default async function Layout({
+ params,
+ children,
+}: PropsWithChildren<{ readonly params: { readonly packageName: string; readonly version: string } }>) {
+ // const fileContent = await readFile(
+ // join(process.cwd(), `../../packages/${params.packageName}/docs/split/${params.version}.dependencies.api.json`),
+ // 'utf8',
+ // );
+ // const dependencies = JSON.parse(fileContent);
+
+ const isMainVersion = params.version === 'main';
+ const fileContent = await fetch(
+ `${process.env.BLOB_STORAGE_URL}/rewrite/${params.packageName}/${params.version}.dependencies.api.json`,
+ { next: isMainVersion ? { revalidate: 0 } : { revalidate: 604_800 } },
+ );
+ const parsedDependencies = await fileContent.json();
+ const dependencies = Object.entries(parsedDependencies)
+ .filter(([key]) => key.startsWith('@discordjs/') && !key.includes('api-extractor'))
+ .map(([key, value]) => `${key.replace('@discordjs/', '').replaceAll('.', '-')}-${value.replaceAll('.', '-')}`);
+
+ // console.log(inspect(dependencies, { depth: 0 }));
+
+ return (
+ // eslint-disable-next-line react/no-unknown-property
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/app/docs/packages/[packageName]/[version]/page.tsx b/apps/website/src/app/docs/packages/[packageName]/[version]/page.tsx
new file mode 100644
index 000000000000..da5a9d94b12e
--- /dev/null
+++ b/apps/website/src/app/docs/packages/[packageName]/[version]/page.tsx
@@ -0,0 +1,49 @@
+import { readFile } from 'node:fs/promises';
+import { join } from 'node:path';
+import rehypeShikiFromHighlighter from '@shikijs/rehype/core';
+import { MDXRemote } from 'next-mdx-remote/rsc';
+import remarkGfm from 'remark-gfm';
+import { getHighlighterCore } from 'shiki/core';
+import getWasm from 'shiki/wasm';
+
+const highlighter = await getHighlighterCore({
+ themes: [import('shiki/themes/vitesse-light.mjs'), import('shiki/themes/vitesse-dark.mjs')],
+ langs: [
+ import('shiki/langs/typescript.mjs'),
+ import('shiki/langs/javascript.mjs'),
+ import('shiki/langs/shellscript.mjs'),
+ ],
+ loadWasm: getWasm,
+});
+
+export default async function Page({ params }: { readonly params: { readonly packageName: string } }) {
+ const fileContent = await readFile(
+ join(process.cwd(), `src/assets/readme/${params.packageName}/home-README.md`),
+ 'utf8',
+ );
+
+ return (
+
+
+
+ );
+}
diff --git a/apps/website/src/app/docs/packages/[package]/[version]/[item]/loading.tsx b/apps/website/src/app/docs/packages/[package]/[version]/[item]/loading.tsx
deleted file mode 100644
index 066281618908..000000000000
--- a/apps/website/src/app/docs/packages/[package]/[version]/[item]/loading.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-export default function Loading() {
- return (
-
- );
-}
diff --git a/apps/website/src/app/docs/packages/[package]/[version]/[item]/not-found.tsx b/apps/website/src/app/docs/packages/[package]/[version]/[item]/not-found.tsx
deleted file mode 100644
index 67ca19a67cd7..000000000000
--- a/apps/website/src/app/docs/packages/[package]/[version]/[item]/not-found.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-'use client';
-
-import type { Route } from 'next';
-import Link from 'next/link';
-import { usePathname } from 'next/navigation';
-
-export default function NotFound() {
- const pathname = usePathname();
- const href = pathname.split('/').slice(0, -1).join('/');
-
- return (
-
-
404
- Not found.
-
- Take me back
-
-
- );
-}
diff --git a/apps/website/src/app/docs/packages/[package]/[version]/[item]/page.tsx b/apps/website/src/app/docs/packages/[package]/[version]/[item]/page.tsx
deleted file mode 100644
index 5503f049ee14..000000000000
--- a/apps/website/src/app/docs/packages/[package]/[version]/[item]/page.tsx
+++ /dev/null
@@ -1,180 +0,0 @@
-import type {
- ApiClass,
- ApiDeclaredItem,
- ApiEnum,
- ApiInterface,
- ApiItem,
- ApiItemContainerMixin,
- ApiMethod,
- ApiMethodSignature,
- ApiProperty,
- ApiPropertySignature,
- ApiTypeAlias,
- ApiVariable,
- ApiFunction,
-} from '@discordjs/api-extractor-model';
-import { ApiItemKind, ApiModel, ApiPackage } from '@discordjs/api-extractor-model';
-import { tryResolveSummaryText } from '@discordjs/scripts';
-import type { Metadata } from 'next';
-import { notFound } from 'next/navigation';
-import { fetchModelJSON } from '~/app/docAPI';
-import { Class } from '~/components/model/Class';
-import { Interface } from '~/components/model/Interface';
-import { TypeAlias } from '~/components/model/TypeAlias';
-import { Variable } from '~/components/model/Variable';
-import { Enum } from '~/components/model/enum/Enum';
-import { Function } from '~/components/model/function/Function';
-import { OVERLOAD_SEPARATOR } from '~/util/constants';
-import { fetchMember } from '~/util/fetchMember';
-import { findMember } from '~/util/model';
-
-export const revalidate = 86_400;
-
-export interface ItemRouteParams {
- item: string;
- package: string;
- version: string;
-}
-
-async function fetchHeadMember({ package: packageName, version, item }: ItemRouteParams) {
- const modelJSON = await fetchModelJSON(packageName, version);
-
- if (!modelJSON) {
- return undefined;
- }
-
- const model = new ApiModel();
- model.addMember(ApiPackage.loadFromJson(modelJSON));
- const pkg = model.tryGetPackageByName(packageName);
- const entry = pkg?.entryPoints[0];
-
- if (!entry) {
- return undefined;
- }
-
- const [memberName] = decodeURIComponent(item).split(OVERLOAD_SEPARATOR);
- return findMember(model, packageName, memberName);
-}
-
-function resolveMemberSearchParams(packageName: string, member?: ApiItem) {
- const params = new URLSearchParams({
- pkg: packageName,
- kind: member?.kind ?? '',
- name: member?.displayName ?? '',
- });
-
- switch (member?.kind) {
- case ApiItemKind.Interface:
- case ApiItemKind.Class: {
- const typedMember = member as ApiItemContainerMixin;
-
- const properties = typedMember.members.filter((member) =>
- [ApiItemKind.Property, ApiItemKind.PropertySignature].includes(member.kind),
- ) as (ApiProperty | ApiPropertySignature)[];
- const methods = typedMember.members.filter((member) =>
- [ApiItemKind.Method, ApiItemKind.Method].includes(member.kind),
- ) as (ApiMethod | ApiMethodSignature)[];
-
- params.append('methods', methods.length.toString());
- params.append('props', properties.length.toString());
- break;
- }
-
- case ApiItemKind.Enum: {
- const typedMember = member as ApiEnum;
- params.append('members', typedMember.members.length.toString());
- break;
- }
-
- default:
- break;
- }
-
- return params;
-}
-
-export async function generateMetadata({ params }: { params: ItemRouteParams }) {
- const member = await fetchHeadMember(params);
- const name = `discord.js${member?.displayName ? ` | ${member.displayName}` : ''}`;
- const ogTitle = `${params.package ?? 'discord.js'}${member?.displayName ? ` | ${member.displayName}` : ''}`;
- const url = new URL('https://discordjs.dev/api/dynamic-open-graph.png');
- const searchParams = resolveMemberSearchParams(params.package, member);
- url.search = searchParams.toString();
- const ogImage = url.toString();
- let description;
-
- if (member) {
- description = tryResolveSummaryText(member as ApiDeclaredItem);
- }
-
- return {
- title: name,
- description: description ?? 'Discord.js API Documentation',
- openGraph: {
- title: ogTitle,
- description: description ?? 'Discord.js API Documentation',
- images: ogImage,
- },
- } satisfies Metadata;
-}
-
-export async function generateStaticParams({ params: { package: packageName, version } }: { params: ItemRouteParams }) {
- if (process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') {
- return [];
- }
-
- const modelJSON = await fetchModelJSON(packageName, version);
-
- if (!modelJSON) {
- return [];
- }
-
- const model = new ApiModel();
- model.addMember(ApiPackage.loadFromJson(modelJSON));
-
- const pkg = model.tryGetPackageByName(packageName);
- const entry = pkg?.entryPoints[0];
-
- if (!entry) {
- return [];
- }
-
- return entry.members.map((member: ApiItem) => ({
- package: packageName,
- version,
- item: `${member.displayName}${OVERLOAD_SEPARATOR}${member.kind}`,
- }));
-}
-
-function Member({ member }: { readonly member?: ApiItem }) {
- switch (member?.kind) {
- case 'Class':
- return ;
- case 'Function':
- return ;
- case 'Interface':
- return ;
- case 'TypeAlias':
- return ;
- case 'Variable':
- return ;
- case 'Enum':
- return ;
- default:
- return Cannot render that item type
;
- }
-}
-
-export default async function Page({ params }: { params: ItemRouteParams }) {
- const member = await fetchMember(params.package, params.version ?? 'main', params.item);
-
- if (!member) {
- notFound();
- }
-
- return (
-
-
-
- );
-}
diff --git a/apps/website/src/app/docs/packages/[package]/[version]/error.tsx b/apps/website/src/app/docs/packages/[package]/[version]/error.tsx
deleted file mode 100644
index bcd4b230ba70..000000000000
--- a/apps/website/src/app/docs/packages/[package]/[version]/error.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-'use client';
-
-export default function Error({ error }: { readonly error: Error }) {
- console.error(error);
-
- return (
-
-
500
- Error.
-
- );
-}
diff --git a/apps/website/src/app/docs/packages/[package]/[version]/layout.tsx b/apps/website/src/app/docs/packages/[package]/[version]/layout.tsx
deleted file mode 100644
index c0278993e4e2..000000000000
--- a/apps/website/src/app/docs/packages/[package]/[version]/layout.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import type { ApiFunction, ApiItem } from '@discordjs/api-extractor-model';
-import { ApiModel, ApiPackage } from '@discordjs/api-extractor-model';
-import dynamic from 'next/dynamic';
-import { notFound } from 'next/navigation';
-import type { PropsWithChildren } from 'react';
-import { fetchModelJSON, fetchVersions } from '~/app/docAPI';
-import { CmdKDialog } from '~/components/CmdK';
-import { Nav } from '~/components/Nav';
-import { Outline } from '~/components/Outline';
-import type { SidebarSectionItemData } from '~/components/Sidebar';
-import { resolveItemURI } from '~/components/documentation/util';
-import { N_RECENT_VERSIONS, PACKAGES } from '~/util/constants';
-import { Providers } from './providers';
-
-const Header = dynamic(async () => import('~/components/Header'));
-const Footer = dynamic(async () => import('~/components/Footer'));
-
-interface VersionRouteParams {
- package: string;
- version: string;
-}
-
-export const generateStaticParams = async () => {
- if (process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') {
- return [];
- }
-
- const params: VersionRouteParams[] = [];
-
- await Promise.all(
- PACKAGES.map(async (packageName) => {
- const versions = (await fetchVersions(packageName)).slice(1, N_RECENT_VERSIONS);
-
- params.push(...versions.map((version) => ({ package: packageName, version })));
- }),
- );
-
- return params;
-};
-
-const serializeIntoSidebarItemData = (item: ApiItem) => {
- return {
- kind: item.kind,
- name: item.displayName,
- href: resolveItemURI(item),
- overloadIndex: 'overloadIndex' in item ? (item.overloadIndex as number) : undefined,
- } as SidebarSectionItemData;
-};
-
-export default async function PackageLayout({ children, params }: PropsWithChildren<{ params: VersionRouteParams }>) {
- const modelJSON = await fetchModelJSON(params.package, params.version);
-
- if (!modelJSON) {
- notFound();
- }
-
- const model = new ApiModel();
- model.addMember(ApiPackage.loadFromJson(modelJSON));
-
- const pkg = model.tryGetPackageByName(params.package);
-
- if (!pkg) {
- notFound();
- }
-
- const entry = pkg.entryPoints[0];
-
- if (!entry) {
- notFound();
- }
-
- const members = entry.members.filter((member) => {
- if (member.kind !== 'Function') {
- return true;
- }
-
- return (member as ApiFunction).overloadIndex === 1;
- });
-
- const versions = await fetchVersions(params.package);
-
- return (
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/website/src/app/docs/packages/[package]/[version]/page.tsx b/apps/website/src/app/docs/packages/[package]/[version]/page.tsx
deleted file mode 100644
index ae328290cca3..000000000000
--- a/apps/website/src/app/docs/packages/[package]/[version]/page.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { readFile } from 'node:fs/promises';
-import { join } from 'node:path';
-import { compileMDX } from 'next-mdx-remote/rsc';
-import { cache } from 'react';
-import rehypeSlug from 'rehype-slug';
-import remarkGfm from 'remark-gfm';
-import { SyntaxHighlighter } from '~/components/SyntaxHighlighter';
-
-interface VersionRouteParams {
- package: string;
- version: string;
-}
-
-const loadREADME = cache(async (packageName: string) => {
- return readFile(join(process.cwd(), 'src', 'assets', 'readme', packageName, 'home-README.md'), 'utf8');
-});
-
-export default async function Page({ params }: { params: VersionRouteParams }) {
- const readmeSource = await loadREADME(params.package);
- const { content } = await compileMDX({
- source: readmeSource,
- // @ts-expect-error SyntaxHighlighter is assignable
- components: { pre: SyntaxHighlighter },
- options: {
- mdxOptions: {
- remarkPlugins: [remarkGfm],
- rehypePlugins: [rehypeSlug],
- format: 'mdx',
- },
- },
- });
-
- return {content}
;
-}
diff --git a/apps/website/src/app/docs/packages/[package]/[version]/providers.tsx b/apps/website/src/app/docs/packages/[package]/[version]/providers.tsx
deleted file mode 100644
index 03367f1761a5..000000000000
--- a/apps/website/src/app/docs/packages/[package]/[version]/providers.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-'use client';
-
-import type { PropsWithChildren } from 'react';
-import { CmdKProvider } from '~/contexts/cmdK';
-import { MemberProvider } from '~/contexts/member';
-import { NavProvider } from '~/contexts/nav';
-import { OutlineProvider } from '~/contexts/outline';
-
-export function Providers({ children }: PropsWithChildren) {
- return (
-
-
-
- {children}
-
-
-
- );
-}
diff --git a/apps/website/src/app/docs/packages/[package]/loading.tsx b/apps/website/src/app/docs/packages/[package]/loading.tsx
deleted file mode 100644
index bde40d687c23..000000000000
--- a/apps/website/src/app/docs/packages/[package]/loading.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '~/app/loading';
diff --git a/apps/website/src/app/docs/packages/[package]/page.tsx b/apps/website/src/app/docs/packages/[package]/page.tsx
deleted file mode 100644
index a92cabe11371..000000000000
--- a/apps/website/src/app/docs/packages/[package]/page.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { VscArrowLeft } from '@react-icons/all-files/vsc/VscArrowLeft';
-import { VscArrowRight } from '@react-icons/all-files/vsc/VscArrowRight';
-import { VscVersions } from '@react-icons/all-files/vsc/VscVersions';
-import Link from 'next/link';
-import { notFound } from 'next/navigation';
-import { fetchVersions } from '~/app/docAPI';
-import { buttonVariants } from '~/styles/Button';
-import { PACKAGES } from '~/util/constants';
-
-export const revalidate = 86_400;
-
-export default async function Page({ params }: { params: { package: string } }) {
- if (!PACKAGES.includes(params.package)) {
- notFound();
- }
-
- const data = await fetchVersions(params.package);
-
- return (
-
-
Select a version:
-
- {data.map((version, idx) => (
-
-
-
- )) ?? null}
-
-
-
Go back
-
-
- );
-}
diff --git a/apps/website/src/app/docs/packages/loading.tsx b/apps/website/src/app/docs/packages/loading.tsx
deleted file mode 100644
index bde40d687c23..000000000000
--- a/apps/website/src/app/docs/packages/loading.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '~/app/loading';
diff --git a/apps/website/src/app/docs/packages/page.tsx b/apps/website/src/app/docs/packages/page.tsx
deleted file mode 100644
index 6eb99fb767fe..000000000000
--- a/apps/website/src/app/docs/packages/page.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { FiExternalLink } from '@react-icons/all-files/fi/FiExternalLink';
-import { VscArrowLeft } from '@react-icons/all-files/vsc/VscArrowLeft';
-import { VscArrowRight } from '@react-icons/all-files/vsc/VscArrowRight';
-import { VscPackage } from '@react-icons/all-files/vsc/VscPackage';
-import Link from 'next/link';
-import { buttonVariants } from '~/styles/Button';
-import { PACKAGES } from '~/util/constants';
-
-export default function Page() {
- return (
-
-
Select a package:
-
-
-
Go back
-
-
- );
-}
diff --git a/apps/website/src/app/error.tsx b/apps/website/src/app/error.tsx
deleted file mode 100644
index 0fb52ed124d9..000000000000
--- a/apps/website/src/app/error.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-'use client';
-
-export default function Error({ error }: { readonly error: Error }) {
- console.error(error);
-
- return (
-
-
500
- Error.
-
- );
-}
diff --git a/apps/website/src/app/layout.tsx b/apps/website/src/app/layout.tsx
index 5c3248efa2ee..82708f046296 100644
--- a/apps/website/src/app/layout.tsx
+++ b/apps/website/src/app/layout.tsx
@@ -1,26 +1,33 @@
import { Analytics } from '@vercel/analytics/react';
+import { GeistMono } from 'geist/font/mono';
+import { GeistSans } from 'geist/font/sans';
import type { Metadata, Viewport } from 'next';
import type { PropsWithChildren } from 'react';
+import { LocalizedStringProvider } from 'react-aria-components/i18n';
import { DESCRIPTION } from '~/util/constants';
-import { inter, jetBrainsMono } from '~/util/fonts';
import { Providers } from './providers';
-import '~/styles/cmdk.css';
import '~/styles/main.css';
+import 'overlayscrollbars/overlayscrollbars.css';
export const viewport: Viewport = {
themeColor: [
- { media: '(prefers-color-scheme: light)', color: '#f1f3f5' },
- { media: '(prefers-color-scheme: dark)', color: '#1c1c1e' },
+ { media: '(prefers-color-scheme: light)', color: '#ffffff' },
+ { media: '(prefers-color-scheme: dark)', color: '#121212' },
],
colorScheme: 'light dark',
};
export const metadata: Metadata = {
metadataBase: new URL(
- process.env.METADATA_BASE_URL ? process.env.METADATA_BASE_URL : `http://localhost:${process.env.PORT ?? 3_000}`,
+ process.env.NEXT_PUBLIC_LOCAL_DEV === 'true'
+ ? `http://localhost:${process.env.PORT ?? 3_000}`
+ : 'https://discord.js.org',
),
- title: 'discord.js',
+ title: {
+ template: '%s | discord.js',
+ default: 'discord.js',
+ },
description: DESCRIPTION,
icons: {
other: [
@@ -66,14 +73,15 @@ export const metadata: Metadata = {
},
other: {
- 'msapplication-TileColor': '#1c1c1e',
+ 'msapplication-TileColor': '#121212',
},
};
-export default function RootLayout({ children }: PropsWithChildren) {
+export default async function RootLayout({ children }: PropsWithChildren) {
return (
-
-
+
+
+
{children}
diff --git a/apps/website/src/app/loading.tsx b/apps/website/src/app/loading.tsx
deleted file mode 100644
index 8852a454635f..000000000000
--- a/apps/website/src/app/loading.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-export default function Loading() {
- return (
-
- );
-}
diff --git a/apps/website/src/app/not-found.tsx b/apps/website/src/app/not-found.tsx
deleted file mode 100644
index d976c343b605..000000000000
--- a/apps/website/src/app/not-found.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import type { Route } from 'next';
-import Link from 'next/link';
-
-export default function NotFound() {
- return (
-
-
404
- Not found.
-
- Take me back
-
-
- );
-}
diff --git a/apps/website/src/app/opengraph-image.tsx b/apps/website/src/app/opengraph-image.tsx
new file mode 100644
index 000000000000..0429e4eece43
--- /dev/null
+++ b/apps/website/src/app/opengraph-image.tsx
@@ -0,0 +1,36 @@
+/* eslint-disable react/no-unknown-property */
+import { ImageResponse } from 'next/og';
+
+export const runtime = 'edge';
+
+export const size = {
+ width: 1_200,
+ height: 630,
+};
+
+export const contentType = 'image/png';
+
+export default async function Image() {
+ return new ImageResponse(
+ (
+
+
+
+
+
+
+ The most popular
+
+
way to build Discord
+
bots.
+
+
+
+
+
+ ),
+ {
+ ...size,
+ },
+ );
+}
diff --git a/apps/website/src/app/page.tsx b/apps/website/src/app/page.tsx
index 3e5d3d780e2f..d367dbf2b079 100644
--- a/apps/website/src/app/page.tsx
+++ b/apps/website/src/app/page.tsx
@@ -1,83 +1,81 @@
-import { FiExternalLink } from '@react-icons/all-files/fi/FiExternalLink';
-import type { Route } from 'next';
+import { ExternalLink } from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link';
import vercelLogo from '~/assets/powered-by-vercel.svg';
import workersLogo from '~/assets/powered-by-workers.png';
-import { InstallButton } from '~/components/InstallButton';
-import { buttonVariants } from '~/styles/Button';
+import { InstallButton } from '~/components/ui/InstallButton';
import { DESCRIPTION } from '~/util/constants';
-export default function Page() {
+export default async function Page() {
return (
-
-
-
-
-
- The most popular way to build
- Discord bots.
-
-
{DESCRIPTION}
-
-
-
-
-
+
+
+
+ The most popular way to build
+ Discord bots.
+
+
{DESCRIPTION}
+
+
+
+
+
+
+
);
diff --git a/apps/website/src/app/providers.tsx b/apps/website/src/app/providers.tsx
index 4a90036e52eb..fbc910a5d1d5 100644
--- a/apps/website/src/app/providers.tsx
+++ b/apps/website/src/app/providers.tsx
@@ -1,13 +1,24 @@
'use client';
+import { Provider as JotaiProvider } from 'jotai';
+import { useRouter } from 'next/navigation';
import { ThemeProvider } from 'next-themes';
import type { PropsWithChildren } from 'react';
+import { RouterProvider } from 'react-aria-components';
import { useSystemThemeFallback } from '~/hooks/useSystemThemeFallback';
import { useUnregisterServiceWorker } from '~/hooks/useUnregisterServiceWorker';
export function Providers({ children }: PropsWithChildren) {
+ const router = useRouter();
useUnregisterServiceWorker();
useSystemThemeFallback();
- return
{children};
+ return (
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+
+
+ {children}
+
+
+ );
}
diff --git a/apps/website/src/assets/fonts/Inter-Black.ttf b/apps/website/src/assets/fonts/Inter-Black.ttf
deleted file mode 100644
index 5aecf7dc414e..000000000000
Binary files a/apps/website/src/assets/fonts/Inter-Black.ttf and /dev/null differ
diff --git a/apps/website/src/assets/fonts/Inter-Bold.ttf b/apps/website/src/assets/fonts/Inter-Bold.ttf
deleted file mode 100644
index 8e82c70d1081..000000000000
Binary files a/apps/website/src/assets/fonts/Inter-Bold.ttf and /dev/null differ
diff --git a/apps/website/src/assets/fonts/Inter-Regular.ttf b/apps/website/src/assets/fonts/Inter-Regular.ttf
deleted file mode 100644
index 8d4eebf20665..000000000000
Binary files a/apps/website/src/assets/fonts/Inter-Regular.ttf and /dev/null differ
diff --git a/apps/website/src/components/Anchor.tsx b/apps/website/src/components/Anchor.tsx
deleted file mode 100644
index b425b8d15e73..000000000000
--- a/apps/website/src/components/Anchor.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { FiLink } from '@react-icons/all-files/fi/FiLink';
-
-export function Anchor({ href }: { readonly href: string }) {
- return (
-
-
-
- );
-}
diff --git a/apps/website/src/components/Badges.tsx b/apps/website/src/components/Badges.tsx
index 4da25e88e570..b040aa0ebc1e 100644
--- a/apps/website/src/components/Badges.tsx
+++ b/apps/website/src/components/Badges.tsx
@@ -1,42 +1,38 @@
-import type { ApiDocumentedItem } from '@discordjs/api-extractor-model';
-import { ApiAbstractMixin, ApiProtectedMixin, ApiReadonlyMixin, ApiStaticMixin } from '@discordjs/api-extractor-model';
+import { AlertTriangle } from 'lucide-react';
import type { PropsWithChildren } from 'react';
-export enum BadgeColor {
- Danger = 'bg-red-500',
- Primary = 'bg-blurple',
- Warning = 'bg-yellow-500',
-}
-
-export function Badge({
- children,
- color = BadgeColor.Primary,
-}: PropsWithChildren<{ readonly color?: BadgeColor | undefined }>) {
+export function Badge({ children, className = '' }: PropsWithChildren<{ readonly className?: string }>) {
return (
{children}
);
}
-export function Badges({ item }: { readonly item: ApiDocumentedItem }) {
- const isStatic = ApiStaticMixin.isBaseClassOf(item) && item.isStatic;
- const isProtected = ApiProtectedMixin.isBaseClassOf(item) && item.isProtected;
- const isReadonly = ApiReadonlyMixin.isBaseClassOf(item) && item.isReadonly;
- const isAbstract = ApiAbstractMixin.isBaseClassOf(item) && item.isAbstract;
- const isDeprecated = Boolean(item.tsdocComment?.deprecatedBlock);
+export async function Badges({ node }: { readonly node: any }) {
+ const isDeprecated = Boolean(node.summary?.deprecatedBlock?.length);
+ const isProtected = node.isProtected;
+ const isStatic = node.isStatic;
+ const isAbstract = node.isAbstract;
+ const isReadonly = node.isReadonly;
+ const isOptional = node.isOptional;
- const isAny = isStatic || isProtected || isReadonly || isAbstract || isDeprecated;
+ const isAny = isDeprecated || isProtected || isStatic || isAbstract || isReadonly || isOptional;
return isAny ? (
-
- {isDeprecated ?
Deprecated : null}
- {isProtected ?
Protected : null}
- {isStatic ?
Static : null}
- {isAbstract ?
Abstract : null}
- {isReadonly ?
Readonly : null}
+
+ {isDeprecated ? (
+
+ deprecated
+
+ ) : null}
+ {isProtected ?
protected : null}
+ {isStatic ?
static : null}
+ {isAbstract ?
abstract : null}
+ {isReadonly ?
readonly : null}
+ {isOptional ?
optional : null}
) : null;
}
diff --git a/apps/website/src/components/CmdK.tsx b/apps/website/src/components/CmdK.tsx
deleted file mode 100644
index 05f44a9f77e1..000000000000
--- a/apps/website/src/components/CmdK.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-'use client';
-
-import type { ApiItemKind } from '@discordjs/api-extractor-model';
-import { VscArrowRight } from '@react-icons/all-files/vsc/VscArrowRight';
-import { VscSymbolClass } from '@react-icons/all-files/vsc/VscSymbolClass';
-import { VscSymbolEnum } from '@react-icons/all-files/vsc/VscSymbolEnum';
-import { VscSymbolEvent } from '@react-icons/all-files/vsc/VscSymbolEvent';
-import { VscSymbolInterface } from '@react-icons/all-files/vsc/VscSymbolInterface';
-import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
-import { VscSymbolProperty } from '@react-icons/all-files/vsc/VscSymbolProperty';
-import { VscSymbolVariable } from '@react-icons/all-files/vsc/VscSymbolVariable';
-import { Dialog } from 'ariakit/dialog';
-import { Command } from 'cmdk';
-import { usePathname, useRouter } from 'next/navigation';
-import { useEffect, useMemo, useState } from 'react';
-import { useKey } from 'react-use';
-import { useCmdK } from '~/contexts/cmdK';
-import { client } from '~/util/search';
-
-function resolveIcon(item: keyof typeof ApiItemKind) {
- switch (item) {
- case 'Class':
- return
;
- case 'Enum':
- return
;
- case 'Interface':
- return
;
- case 'Property':
- return
;
- case 'TypeAlias':
- return
;
- case 'Variable':
- return
;
- case 'Event':
- return
;
- default:
- return
;
- }
-}
-
-export function CmdKDialog() {
- const pathname = usePathname();
- const router = useRouter();
- const dialog = useCmdK();
- const [search, setSearch] = useState('');
- const [searchResults, setSearchResults] = useState
([]);
-
- const packageName = pathname?.split('/').slice(3, 4)[0];
- const branchName = pathname?.split('/').slice(4, 5)[0];
-
- const searchResultItems = useMemo(
- () =>
- searchResults?.map((item, idx) => (
- {
- router.push(item.path);
- dialog!.setOpen(false);
- }}
- value={`${item.id}`}
- >
-
-
- {resolveIcon(item.kind)}
-
-
{item.name}
-
{item.summary}
-
- {item.path}
-
-
-
-
-
-
- )) ?? [],
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [searchResults],
- );
-
- useKey(
- (event) => {
- if (event.key === 'k' && (event.metaKey || event.ctrlKey)) {
- event.preventDefault();
- return true;
- }
-
- return false;
- },
- dialog!.toggle,
- { event: 'keydown', options: {} },
- [],
- );
-
- useEffect(() => {
- if (!dialog!.open) {
- setSearch('');
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [dialog!.open]);
-
- useEffect(() => {
- const searchDoc = async (searchString: string, version: string) => {
- const res = await client
- .index(`${packageName?.replaceAll('.', '-')}-${version}`)
- .search(searchString, { limit: 5 });
- setSearchResults(res.hits);
- };
-
- if (search && packageName) {
- void searchDoc(search, branchName?.replaceAll('.', '-') ?? 'main');
- } else {
- setSearchResults([]);
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [search]);
-
- return (
-
- );
-}
diff --git a/apps/website/src/components/CodeHeading.tsx b/apps/website/src/components/CodeHeading.tsx
deleted file mode 100644
index 655de27f3480..000000000000
--- a/apps/website/src/components/CodeHeading.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import type { ReactNode } from 'react';
-import { Anchor } from './Anchor';
-import { SourceLink } from './documentation/SourceLink';
-
-export interface CodeListingProps {
- /**
- * The value of this heading.
- */
- readonly children: ReactNode;
- /**
- * Additional class names to apply to the root element.
- */
- readonly className?: string | undefined;
- /**
- * The href of this heading.
- */
- readonly href?: string | undefined;
- /**
- * The line in the source code where this part is declared
- */
- readonly sourceLine?: number | undefined;
- /**
- * The URL of the source code of this code part
- */
- readonly sourceURL?: string | undefined;
-}
-
-export function CodeHeading({ href, className, children, sourceURL, sourceLine }: CodeListingProps) {
- return (
-
-
- {href ?
: null}
- {children}
-
- {sourceURL ?
: null}
-
- );
-}
diff --git a/apps/website/src/components/ConstructorNode.tsx b/apps/website/src/components/ConstructorNode.tsx
new file mode 100644
index 000000000000..3ff4e3c20083
--- /dev/null
+++ b/apps/website/src/components/ConstructorNode.tsx
@@ -0,0 +1,48 @@
+import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
+import { Code2, LinkIcon } from 'lucide-react';
+import Link from 'next/link';
+import { ParameterNode } from './ParameterNode';
+import { SummaryNode } from './SummaryNode';
+
+export async function ConstructorNode({ node, version }: { readonly node: any; readonly version: string }) {
+ return (
+
+
+
+ Constructors
+
+
+
+
+ {/* constructor({parsedContent.constructor.parametersString}) */}
+
+
+
+ constructor({node.parameters?.length ? : null})
+
+
+
+
+
+
+
+ {node.summary?.summarySection.length ? (
+
+ ) : null}
+
+
+
+ );
+}
diff --git a/apps/website/src/components/DeprecatedNode.tsx b/apps/website/src/components/DeprecatedNode.tsx
new file mode 100644
index 000000000000..414639d1f2ef
--- /dev/null
+++ b/apps/website/src/components/DeprecatedNode.tsx
@@ -0,0 +1,18 @@
+import { DocNode } from './DocNode';
+import { Alert } from './ui/Alert';
+
+export async function DeprecatedNode({
+ deprecatedBlock,
+ version,
+}: {
+ readonly deprecatedBlock: any;
+ readonly version: string;
+}) {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/DocItem.tsx b/apps/website/src/components/DocItem.tsx
new file mode 100644
index 000000000000..ce4d7b10425d
--- /dev/null
+++ b/apps/website/src/components/DocItem.tsx
@@ -0,0 +1,134 @@
+import { VscSymbolParameter } from '@react-icons/all-files/vsc/VscSymbolParameter';
+import { ConstructorNode } from './ConstructorNode';
+import { DeprecatedNode } from './DeprecatedNode';
+import { EnumMemberNode } from './EnumMemberNode';
+import { EventNode } from './EventNode';
+import { InformationNode } from './InformationNode';
+import { MethodNode } from './MethodNode';
+import { Outline } from './Outline';
+import { OverlayScrollbarsComponent } from './OverlayScrollbars';
+import { ParameterNode } from './ParameterNode';
+import { PropertyNode } from './PropertyNode';
+import { ReturnNode } from './ReturnNode';
+import { SeeNode } from './SeeNode';
+import { SummaryNode } from './SummaryNode';
+import { SyntaxHighlighter } from './SyntaxHighlighter';
+import { TypeParameterNode } from './TypeParameterNode';
+import { UnionMember } from './UnionMember';
+import { Tab, TabList, TabPanel, Tabs } from './ui/Tabs';
+
+async function OverloadNode({ node, packageName, version }: { node: any; packageName: string; version: string }) {
+ return (
+
+
+ {node.overloads.map((overload: any) => {
+ return (
+
+ Overload {overload.overloadIndex}
+
+ );
+ })}
+
+ {node.overloads.map((overload: any) => {
+ return (
+
+
+
+ );
+ })}
+
+ );
+}
+
+export function DocItem({
+ node,
+ packageName,
+ version,
+}: {
+ readonly node: any;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ if (node.overloads?.length) {
+ return ;
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+ {node.summary?.deprecatedBlock.length ? (
+
+ ) : null}
+
+ {node.summary?.summarySection ? : null}
+
+ {node.summary?.returnsBlock.length ? : null}
+
+ {node.summary?.seeBlocks.length ? : null}
+
+
+
+ {node.constructor?.parametersString ? : null}
+
+ {node.typeParameters?.length ? (
+
+
+
+ Type Parameters
+
+
+
+ ) : null}
+
+ {node.parameters?.length ? (
+
+ ) : null}
+
+ {node.members?.properties?.length ? (
+
+ ) : null}
+
+ {node.members?.events?.length ? (
+
+
+
+ ) : null}
+
+ {node.members?.methods?.length ? (
+
+
+
+ ) : null}
+
+ {node.members?.length ? : null}
+
+ {node.unionMembers?.length ? : null}
+ >
+ );
+}
diff --git a/apps/website/src/components/DocKind.tsx b/apps/website/src/components/DocKind.tsx
new file mode 100644
index 000000000000..47ca84732964
--- /dev/null
+++ b/apps/website/src/components/DocKind.tsx
@@ -0,0 +1,44 @@
+export function resolveNodeKind(kind: string) {
+ switch (kind) {
+ case 'Class':
+ return {
+ text: 'text-green-500',
+ background: 'bg-green-500/20',
+ };
+ case 'Interface':
+ return {
+ text: 'text-amber-500',
+ background: 'bg-amber-500/20',
+ };
+ case 'Function':
+ return {
+ text: 'text-blue-500',
+ background: 'bg-blue-500/20',
+ };
+ case 'Enum':
+ return {
+ text: 'text-rose-500',
+ background: 'bg-rose-500/20',
+ };
+ case 'TypeAlias':
+ return {
+ text: 'text-pink-500',
+ background: 'bg-pink-500/20',
+ };
+ case 'Variable':
+ return {
+ text: 'text-purple-500',
+ background: 'bg-purple-500/20',
+ };
+ default:
+ return {
+ text: 'text-gray-500',
+ background: 'bg-gray-500/20',
+ };
+ }
+}
+
+export async function DocKind({ background = false, node }: { readonly background?: boolean; readonly node: any }) {
+ const kind = resolveNodeKind(node.kind);
+ return {node.kind.toLowerCase()};
+}
diff --git a/apps/website/src/components/DocNode.tsx b/apps/website/src/components/DocNode.tsx
new file mode 100644
index 000000000000..112200c758db
--- /dev/null
+++ b/apps/website/src/components/DocNode.tsx
@@ -0,0 +1,72 @@
+import Link from 'next/link';
+import { OverlayScrollbarsComponent } from './OverlayScrollbars';
+import { SyntaxHighlighter } from './SyntaxHighlighter';
+
+export async function DocNode({ node, version }: { readonly node?: any; readonly version: string }) {
+ const createNode = (node: any, idx: number) => {
+ switch (node.kind) {
+ case 'PlainText':
+ return {node.text};
+ case 'LinkTag': {
+ if (node.resolvedPackage) {
+ return (
+
+ {node.text}
+
+ );
+ }
+
+ if (node.uri) {
+ return (
+
+ {node.text}
+
+ );
+ }
+
+ return {node.text};
+ }
+
+ case 'CodeSpan':
+ return (
+
+ {node.text}
+
+ );
+
+ case 'FencedCode': {
+ const { language, text } = node;
+
+ return (
+
+
+
+ );
+ }
+
+ case 'SoftBreak':
+ return null;
+ default:
+ return null;
+ }
+ };
+
+ return node?.map(createNode) ?? null;
+}
diff --git a/apps/website/src/components/DocumentationLink.tsx b/apps/website/src/components/DocumentationLink.tsx
deleted file mode 100644
index bef2aa71ac38..000000000000
--- a/apps/website/src/components/DocumentationLink.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import type { PropsWithChildren } from 'react';
-
-export function DocumentationLink({ children, href }: PropsWithChildren<{ readonly href: string }>) {
- return (
-
- {children}
-
- );
-}
diff --git a/apps/website/src/components/EnumMemberNode.tsx b/apps/website/src/components/EnumMemberNode.tsx
new file mode 100644
index 000000000000..06f5f9a13e34
--- /dev/null
+++ b/apps/website/src/components/EnumMemberNode.tsx
@@ -0,0 +1,109 @@
+import { VscSymbolEnumMember } from '@react-icons/all-files/vsc/VscSymbolEnumMember';
+import { Code2, LinkIcon } from 'lucide-react';
+import Link from 'next/link';
+import { Fragment } from 'react';
+import { Badges } from './Badges';
+import { DeprecatedNode } from './DeprecatedNode';
+import { ExampleNode } from './ExampleNode';
+import { ExcerptNode } from './ExcerptNode';
+import { InheritedFromNode } from './InheritedFromNode';
+import { ParameterNode } from './ParameterNode';
+import { ReturnNode } from './ReturnNode';
+import { SeeNode } from './SeeNode';
+import { SummaryNode } from './SummaryNode';
+
+export async function EnumMemberNode({
+ node,
+ packageName,
+ version,
+}: {
+ readonly node: any;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ return (
+
+
+
+ Members
+
+
+
+ {node.map((enumMember: any, idx: number) => {
+ return (
+
+
+
+
+
+
+
+
+
+ {enumMember.displayName}
+ {enumMember.parameters?.length ? (
+
+ ) : null}
+ {enumMember.initializerExcerpt ? (
+ <>
+ {' = '}
+
+ >
+ ) : null}
+
+
+
+
+
+
+
+
+ {enumMember.summary?.deprecatedBlock.length ? (
+
+ ) : null}
+
+ {enumMember.summary?.summarySection.length ? (
+
+ ) : null}
+
+ {enumMember.summary?.exampleBlocks.length ? (
+
+ ) : null}
+
+ {enumMember.summary?.returnsBlock.length ? (
+
+ ) : null}
+
+ {enumMember.inheritedFrom ? (
+
+ ) : null}
+
+ {enumMember.summary?.seeBlocks.length ? (
+
+ ) : null}
+
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/apps/website/src/components/EventNode.tsx b/apps/website/src/components/EventNode.tsx
new file mode 100644
index 000000000000..f9a7e109714d
--- /dev/null
+++ b/apps/website/src/components/EventNode.tsx
@@ -0,0 +1,170 @@
+import { VscSymbolEvent } from '@react-icons/all-files/vsc/VscSymbolEvent';
+import { ChevronDown, ChevronUp, Code2 } from 'lucide-react';
+import { Badges } from './Badges';
+import { DeprecatedNode } from './DeprecatedNode';
+import { ExampleNode } from './ExampleNode';
+import { InheritedFromNode } from './InheritedFromNode';
+import { ParameterNode } from './ParameterNode';
+import { ReturnNode } from './ReturnNode';
+import { SeeNode } from './SeeNode';
+import { SummaryNode } from './SummaryNode';
+import { TypeParameterNode } from './TypeParameterNode';
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/Collapsible';
+import { Tab, TabList, TabPanel, Tabs } from './ui/Tabs';
+
+async function EventBodyNode({
+ event,
+ packageName,
+ version,
+ overload = false,
+}: {
+ readonly event: any;
+ readonly overload?: boolean;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ return (
+ <>
+
+
+
+ {event.displayName}
+ {event.typeParameters?.length ? (
+ <>
+ {'<'}
+
+ {'>'}
+ >
+ ) : null}
+ ({event.parameters?.length ? : null})
+
+
+
+
+
+
+
+ {event.summary?.deprecatedBlock.length ? (
+
+ ) : null}
+
+ {event.summary?.summarySection.length ? (
+
+ ) : null}
+
+ {event.summary?.exampleBlocks.length ? (
+
+ ) : null}
+
+ {event.summary?.returnsBlock.length ? (
+
+ ) : null}
+
+ {event.inheritedFrom ? (
+
+ ) : null}
+
+ {event.summary?.seeBlocks.length ?
: null}
+
+
+ >
+ );
+}
+
+async function OverloadNode({
+ event,
+ packageName,
+ version,
+}: {
+ readonly event: any;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ return (
+
+
+ {event.overloads.map((overload: any) => {
+ return (
+
+ Overload {overload.overloadIndex}
+
+ );
+ })}
+
+ {event.overloads.map((overload: any) => {
+ return (
+
+
+
+ );
+ })}
+
+ );
+}
+
+export async function EventNode({
+ node,
+ packageName,
+ version,
+}: {
+ readonly node: any;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ return (
+
+
+
+ Events
+
+
+
+
+
+
+
+ {node.map((event: any) => {
+ return event.overloads?.length ? (
+
+ ) : (
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/apps/website/src/components/ExampleNode.tsx b/apps/website/src/components/ExampleNode.tsx
new file mode 100644
index 000000000000..a8692cc65037
--- /dev/null
+++ b/apps/website/src/components/ExampleNode.tsx
@@ -0,0 +1,10 @@
+import { DocNode } from './DocNode';
+
+export async function ExampleNode({ node, version }: { readonly node: any; readonly version: string }) {
+ return (
+
+ Examples:
+
+
+ );
+}
diff --git a/apps/website/src/components/ExcerptNode.tsx b/apps/website/src/components/ExcerptNode.tsx
new file mode 100644
index 000000000000..adee4a59e9d3
--- /dev/null
+++ b/apps/website/src/components/ExcerptNode.tsx
@@ -0,0 +1,66 @@
+import Link from 'next/link';
+import { Fragment } from 'react';
+import { BuiltinDocumentationLinks } from '~/util/builtinDocumentationLinks';
+
+export async function ExcerptNode({ node, version }: { readonly node?: any; readonly version: string }) {
+ const createExcerpt = (excerpts: any) => {
+ const excerpt = Array.isArray(excerpts) ? excerpts : excerpts.excerpts ?? [excerpts];
+
+ return (
+
+ {excerpt.map((excerpt: any, idx: number) => {
+ if (excerpt.resolvedItem) {
+ return (
+
+ {excerpt.text}
+
+ );
+ }
+
+ if (excerpt.href) {
+ return (
+
+ {excerpt.text}
+
+ );
+ }
+
+ if (excerpt.text in BuiltinDocumentationLinks) {
+ const href = BuiltinDocumentationLinks[excerpt.text as keyof typeof BuiltinDocumentationLinks];
+ return (
+
+ {excerpt.text}
+
+ );
+ }
+
+ return {excerpt.text};
+ })}
+
+ );
+ };
+
+ return node?.map(createExcerpt) ?? null;
+}
diff --git a/apps/website/src/components/ExcerptText.tsx b/apps/website/src/components/ExcerptText.tsx
deleted file mode 100644
index 64abcea04080..000000000000
--- a/apps/website/src/components/ExcerptText.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import type { ApiPackage, Excerpt } from '@discordjs/api-extractor-model';
-import { ExcerptTokenKind } from '@discordjs/api-extractor-model';
-import { BuiltinDocumentationLinks } from '~/util/builtinDocumentationLinks';
-import { DISCORD_API_TYPES_DOCS_URL } from '~/util/constants';
-import { DocumentationLink } from './DocumentationLink';
-import { ItemLink } from './ItemLink';
-import { resolveCanonicalReference, resolveItemURI } from './documentation/util';
-
-export interface ExcerptTextProps {
- /**
- * The package this excerpt is referenced from.
- */
- readonly apiPackage: ApiPackage;
-
- /**
- * The tokens to render.
- */
- readonly excerpt: Excerpt;
-}
-
-/**
- * A component that renders excerpt tokens from an api item.
- */
-export function ExcerptText({ excerpt, apiPackage }: ExcerptTextProps) {
- return (
-
- {excerpt.spannedTokens.map((token, idx) => {
- if (token.kind === ExcerptTokenKind.Reference) {
- if (token.text in BuiltinDocumentationLinks) {
- const href = BuiltinDocumentationLinks[token.text as keyof typeof BuiltinDocumentationLinks];
- return (
-
- {token.text}
-
- );
- }
-
- const source = token.canonicalReference?.source;
- const symbol = token.canonicalReference?.symbol;
- if (source && 'packageName' in source && source.packageName === 'discord-api-types' && symbol) {
- const { meaning, componentPath: path } = symbol;
- let href = DISCORD_API_TYPES_DOCS_URL;
-
- // dapi-types doesn't have routes for class members
- // so we can assume this member is for an enum
- if (meaning === 'member' && path && 'parent' in path) {
- href += `/enum/${path.parent}#${path.component}`;
- } else if (meaning === 'type' || meaning === 'var') {
- href += `#${token.text}`;
- } else {
- href += `/${meaning}/${token.text}`;
- }
-
- return (
-
- {token.text}
-
- );
- }
-
- const resolved = token.canonicalReference
- ? resolveCanonicalReference(token.canonicalReference, apiPackage)
- : null;
-
- if (!resolved) {
- return token.text;
- }
-
- return (
-
- {token.text}
-
- );
- }
-
- return token.text.replace(/import\("discord-api-types(?:\/v\d+)?"\)\./, '');
- })}
-
- );
-}
diff --git a/apps/website/src/components/Header.tsx b/apps/website/src/components/Header.tsx
deleted file mode 100644
index dab90e015b1c..000000000000
--- a/apps/website/src/components/Header.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-'use client';
-
-import { FiCommand } from '@react-icons/all-files/fi/FiCommand';
-import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted';
-import { VscMenu } from '@react-icons/all-files/vsc/VscMenu';
-import { VscSearch } from '@react-icons/all-files/vsc/VscSearch';
-import { Button } from 'ariakit/button';
-import type { Route } from 'next';
-import dynamic from 'next/dynamic';
-import Link from 'next/link';
-import { usePathname } from 'next/navigation';
-import { Fragment, useMemo } from 'react';
-import { useCmdK } from '~/contexts/cmdK';
-import { useNav } from '~/contexts/nav';
-
-const ThemeSwitcher = dynamic(async () => import('./ThemeSwitcher'));
-
-export default function Header() {
- const pathname = usePathname();
- const { setOpened } = useNav();
- const dialog = useCmdK();
-
- const pathElements = useMemo(
- () =>
- pathname
- .split('/')
- .slice(1)
- .map((path, idx, original) => (
-
- {path}
-
- )),
- [pathname],
- );
-
- const breadcrumbs = useMemo(
- () =>
- pathElements.flatMap((el, idx, array) => {
- if (idx === 0) {
- return (
-
- /
- {el}
- /
-
- );
- }
-
- if (idx !== array.length - 1) {
- return (
-
- {el}
- /
-
- );
- }
-
- return {el}
;
- }),
- [pathElements],
- );
-
- return (
-
-
-
-
-
{breadcrumbs}
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/website/src/components/InformationNode.tsx b/apps/website/src/components/InformationNode.tsx
new file mode 100644
index 000000000000..9e8852f5a36e
--- /dev/null
+++ b/apps/website/src/components/InformationNode.tsx
@@ -0,0 +1,33 @@
+import { FileCode2 } from 'lucide-react';
+import { Badges } from './Badges';
+import { DocKind } from './DocKind';
+import { InheritanceNode } from './InheritanceNode';
+
+export async function InformationNode({ node, version }: { readonly node: any; readonly version: string }) {
+ return (
+
+
+
+ {node.displayName}
+
+ {node.implements ? : null}
+ {node.extends ? : null}
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/InheritanceNode.tsx b/apps/website/src/components/InheritanceNode.tsx
new file mode 100644
index 000000000000..8b2983979492
--- /dev/null
+++ b/apps/website/src/components/InheritanceNode.tsx
@@ -0,0 +1,20 @@
+import { ExcerptNode } from './ExcerptNode';
+
+export async function InheritanceNode({
+ text,
+ node,
+ version,
+}: {
+ readonly node: any;
+ readonly text: string;
+ readonly version: string;
+}) {
+ return (
+
+
{text}
{' '}
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/InheritanceText.tsx b/apps/website/src/components/InheritanceText.tsx
deleted file mode 100644
index cacefe71e49d..000000000000
--- a/apps/website/src/components/InheritanceText.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import type { ApiDeclaredItem } from '@discordjs/api-extractor-model';
-import { ItemLink } from './ItemLink';
-import { resolveItemURI } from './documentation/util';
-
-export function InheritanceText({ parent }: { readonly parent: ApiDeclaredItem }) {
- return (
-
- Inherited from{' '}
-
- {parent.displayName}
-
-
- );
-}
diff --git a/apps/website/src/components/InheritedFromNode.tsx b/apps/website/src/components/InheritedFromNode.tsx
new file mode 100644
index 000000000000..235882cf2372
--- /dev/null
+++ b/apps/website/src/components/InheritedFromNode.tsx
@@ -0,0 +1,23 @@
+import Link from 'next/link';
+
+export async function InheritedFromNode({
+ node,
+ packageName,
+ version,
+}: {
+ readonly node: any;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ return (
+
+ Inherited from:{' '}
+
+ {node.slice(0, node.indexOf(':'))}
+
+
+ );
+}
diff --git a/apps/website/src/components/InstallButton.tsx b/apps/website/src/components/InstallButton.tsx
deleted file mode 100644
index 740cf6dca397..000000000000
--- a/apps/website/src/components/InstallButton.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-'use client';
-
-import { FiCheck } from '@react-icons/all-files/fi/FiCheck';
-import { FiCopy } from '@react-icons/all-files/fi/FiCopy';
-import { useEffect, useState } from 'react';
-import { useCopyToClipboard } from 'react-use';
-import { buttonVariants } from '~/styles/Button';
-
-export function InstallButton() {
- const [interacted, setInteracted] = useState(false);
- const [state, copyToClipboard] = useCopyToClipboard();
-
- useEffect(() => {
- const timer = setTimeout(() => setInteracted(false), 2_000);
- return () => clearTimeout(timer);
- }, [interacted]);
-
- return (
-
- );
-}
diff --git a/apps/website/src/components/ItemLink.tsx b/apps/website/src/components/ItemLink.tsx
deleted file mode 100644
index d54082be2c8a..000000000000
--- a/apps/website/src/components/ItemLink.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-'use client';
-
-import type { LinkProps } from 'next/link';
-import Link from 'next/link';
-import { usePathname } from 'next/navigation';
-import type { PropsWithChildren } from 'react';
-import { useCurrentPathMeta } from '~/hooks/useCurrentPathMeta';
-
-export interface ItemLinkProps extends Omit, 'href'> {
- readonly className?: string;
- /**
- * The URI of the api item to link to. (e.g. `/RestManager`)
- */
- readonly itemURI: string;
-
- /**
- * The name of the package the item belongs to.
- */
- readonly packageName?: string | undefined;
-
- // TODO: This needs to be properly typed above but monkey-patching it for now.
- readonly title?: string | undefined;
-
- /**
- * The version of the package the item belongs to.
- */
- readonly version?: string | undefined;
-}
-
-/**
- * A component that renders a link to an api item.
- *
- * @remarks
- * This component only needs the relative path to the item, and will automatically
- * generate the full path to the item client-side.
- */
-export function ItemLink(props: PropsWithChildren>) {
- const pathname = usePathname();
- const { packageName, version } = useCurrentPathMeta();
-
- if (!pathname) {
- throw new Error('ItemLink must be used inside a Next.js page. (e.g. /docs/packages/foo/main)');
- }
-
- const { itemURI, packageName: pkgName, version: pkgVersion, ...linkProps } = props;
-
- return ;
-}
diff --git a/apps/website/src/components/MethodNode.tsx b/apps/website/src/components/MethodNode.tsx
new file mode 100644
index 000000000000..5a9d541fa89d
--- /dev/null
+++ b/apps/website/src/components/MethodNode.tsx
@@ -0,0 +1,180 @@
+import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
+import { ChevronDown, ChevronUp, Code2, LinkIcon } from 'lucide-react';
+import Link from 'next/link';
+import { Badges } from './Badges';
+import { DeprecatedNode } from './DeprecatedNode';
+import { ExampleNode } from './ExampleNode';
+import { ExcerptNode } from './ExcerptNode';
+import { InheritedFromNode } from './InheritedFromNode';
+import { ParameterNode } from './ParameterNode';
+import { ReturnNode } from './ReturnNode';
+import { SeeNode } from './SeeNode';
+import { SummaryNode } from './SummaryNode';
+import { TypeParameterNode } from './TypeParameterNode';
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/Collapsible';
+import { Tab, TabList, TabPanel, Tabs } from './ui/Tabs';
+
+async function MethodBodyNode({
+ method,
+ packageName,
+ version,
+ overload = false,
+}: {
+ readonly method: any;
+ readonly overload?: boolean;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ return (
+ <>
+
+
+
+ {method.displayName}
+
+
+
+
+ {method.typeParameters?.length ? (
+ <>
+ {'<'}
+
+ {'>'}
+ >
+ ) : null}
+ ({method.parameters?.length ? : null}
+ ) :
+
+
+
+
+
+
+
+
+ {method.summary?.deprecatedBlock.length ? (
+
+ ) : null}
+
+ {method.summary?.summarySection.length ? (
+
+ ) : null}
+
+ {method.summary?.exampleBlocks.length ? (
+
+ ) : null}
+
+ {method.summary?.returnsBlock.length ? (
+
+ ) : null}
+
+ {method.inheritedFrom ? (
+
+ ) : null}
+
+ {method.summary?.seeBlocks.length ? (
+
+ ) : null}
+
+
+ >
+ );
+}
+
+async function OverloadNode({
+ method,
+ packageName,
+ version,
+}: {
+ readonly method: any;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ return (
+
+
+ {method.overloads.map((overload: any) => {
+ return (
+
+ Overload {overload.overloadIndex}
+
+ );
+ })}
+
+ {method.overloads.map((overload: any) => {
+ return (
+
+
+
+ );
+ })}
+
+ );
+}
+
+export async function MethodNode({
+ node,
+ packageName,
+ version,
+}: {
+ readonly node: any;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ return (
+
+
+
+ Methods
+
+
+
+
+
+
+
+ {node.map((method: any) => {
+ return method.overloads?.length ? (
+
+ ) : (
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/apps/website/src/components/Nav.tsx b/apps/website/src/components/Nav.tsx
deleted file mode 100644
index dbb08aa2ce9c..000000000000
--- a/apps/website/src/components/Nav.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-'use client';
-
-import dynamic from 'next/dynamic';
-import { Scrollbars } from 'react-custom-scrollbars-2';
-import { useNav } from '~/contexts/nav';
-import { Sidebar } from './Sidebar';
-import type { SidebarSectionItemData } from './Sidebar';
-
-const PackageSelect = dynamic(async () => import('./PackageSelect'));
-const VersionSelect = dynamic(async () => import('./VersionSelect'));
-
-export function Nav({
- members,
- versions,
-}: {
- readonly members: SidebarSectionItemData[];
- readonly versions: string[];
-}) {
- const { opened } = useNav();
-
- return (
-
- );
-}
diff --git a/apps/website/src/components/Navigation.tsx b/apps/website/src/components/Navigation.tsx
new file mode 100644
index 000000000000..466e6a98c5a0
--- /dev/null
+++ b/apps/website/src/components/Navigation.tsx
@@ -0,0 +1,227 @@
+import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted';
+import { ChevronDown, ChevronUp } from 'lucide-react';
+import dynamic from 'next/dynamic';
+import Link from 'next/link';
+import { fetchVersions } from '~/util/fetchVersions';
+import { resolveNodeKind } from './DocKind';
+import { NavigationItem } from './NavigationItem';
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/Collapsible';
+import { PackageSelect } from './ui/PackageSelect';
+import { SearchButton } from './ui/SearchButton';
+import { VersionSelect } from './ui/VersionSelect';
+
+// eslint-disable-next-line promise/prefer-await-to-then
+const ThemeSwitch = dynamic(async () => import('~/components/ui/ThemeSwitch').then((mod) => mod.ThemeSwitch), {
+ ssr: false,
+});
+
+export async function Navigation({
+ className = '',
+ packageName,
+ version,
+ drawer = false,
+}: {
+ readonly className?: string;
+ readonly drawer?: boolean;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ const isMainVersion = version === 'main';
+ const fileContent = await fetch(
+ `${process.env.BLOB_STORAGE_URL}/rewrite/${packageName}/${version}.sitemap.api.json`,
+ { next: isMainVersion ? { revalidate: 0 } : { revalidate: 604_800 } },
+ );
+ const node = await fileContent.json();
+
+ const versions = await fetchVersions(packageName);
+
+ const groupedNodes = node.reduce((acc: any, node: any) => {
+ (acc[node.kind.toLowerCase()] ||= []).push(node);
+ return acc;
+ }, {});
+
+ return (
+
+ );
+}
diff --git a/apps/website/src/components/NavigationItem.tsx b/apps/website/src/components/NavigationItem.tsx
new file mode 100644
index 000000000000..716262897356
--- /dev/null
+++ b/apps/website/src/components/NavigationItem.tsx
@@ -0,0 +1,34 @@
+'use client';
+
+import { useSetAtom } from 'jotai';
+import Link from 'next/link';
+import { usePathname } from 'next/navigation';
+import type { PropsWithChildren } from 'react';
+import { isDrawerOpenAtom } from '~/stores/drawer';
+
+export function NavigationItem({
+ node,
+ packageName,
+ version,
+ children,
+}: PropsWithChildren<{
+ readonly node: any;
+ readonly packageName: string;
+ readonly version: string;
+}>) {
+ const pathname = usePathname();
+ const setDrawerOpen = useSetAtom(isDrawerOpenAtom);
+
+ const href = `/docs/packages/${packageName}/${version}/${node.href}`;
+
+ return (
+ setDrawerOpen(false)}
+ >
+ {children}
+
+ );
+}
diff --git a/apps/website/src/components/Outline.tsx b/apps/website/src/components/Outline.tsx
index f40460e92cde..44fd1d710815 100644
--- a/apps/website/src/components/Outline.tsx
+++ b/apps/website/src/components/Outline.tsx
@@ -1,32 +1,135 @@
-'use client';
-
-import { useOutline } from '~/contexts/outline';
-import { Scrollbars } from './Scrollbars';
-import { TableOfContentItems } from './TableOfContentItems';
-
-export function Outline() {
- const { members } = useOutline();
-
- if (!members) {
- return null;
- }
-
- return (
-
-
-
- );
+import { VscListSelection } from '@react-icons/all-files/vsc/VscListSelection';
+import { VscSymbolEvent } from '@react-icons/all-files/vsc/VscSymbolEvent';
+import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
+import { VscSymbolProperty } from '@react-icons/all-files/vsc/VscSymbolProperty';
+import { ChevronDown, ChevronUp } from 'lucide-react';
+import Link from 'next/link';
+import { Fragment } from 'react';
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/Collapsible';
+
+export async function Outline({ node }: { readonly node: any }) {
+ const hasAny = node.members?.properties?.length || node.members?.events?.length || node.members?.methods?.length;
+
+ return hasAny ? (
+
+
+
+ Table of contents
+
+
+
+
+
+
+
+
+ {node.members?.properties?.length ? (
+
+
+
+
+ Properties
+
+
+
+
+
+
+
+ {node.members.properties.map((property: any, idx: number) => {
+ return (
+
+
+
+
+ {property.displayName}
+
+
+
+
+ );
+ })}
+
+
+
+ ) : null}
+
+ {node.members?.events?.length ? (
+
+
+
+
+ Events
+
+
+
+
+
+
+
+ {node.members.events.map((event: any, idx: number) => {
+ return (
+
+
+
+
+ {event.displayName}
+
+
+
+
+ );
+ })}
+
+
+
+ ) : null}
+
+ {node.members?.methods?.length ? (
+
+
+
+
+ Methods
+
+
+
+
+
+
+
+ {node.members.methods.map((method: any, idx: number) => {
+ return (
+
+
+
+
+ {method.displayName}
+
+
+
+
+ );
+ })}
+
+
+
+ ) : null}
+
+
+
+
+
+ ) : null;
}
diff --git a/apps/website/src/components/OverlayScrollbars.tsx b/apps/website/src/components/OverlayScrollbars.tsx
new file mode 100644
index 000000000000..0e37086c2edc
--- /dev/null
+++ b/apps/website/src/components/OverlayScrollbars.tsx
@@ -0,0 +1,7 @@
+'use client';
+
+import { OverlayScrollbars, ClickScrollPlugin } from 'overlayscrollbars';
+
+OverlayScrollbars.plugin(ClickScrollPlugin);
+
+export { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
diff --git a/apps/website/src/components/OverloadSwitcher.tsx b/apps/website/src/components/OverloadSwitcher.tsx
deleted file mode 100644
index 923cb645eb48..000000000000
--- a/apps/website/src/components/OverloadSwitcher.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-'use client';
-
-import { VscChevronDown } from '@react-icons/all-files/vsc/VscChevronDown';
-import { VscVersions } from '@react-icons/all-files/vsc/VscVersions';
-import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
-import type { PropsWithChildren, ReactNode } from 'react';
-import { useCallback, useMemo, useState, useEffect } from 'react';
-
-export interface OverloadSwitcherProps {
- methodName: string;
- overloads: ReactNode[];
-}
-
-export default function OverloadSwitcher({
- methodName,
- overloads,
- children,
-}: PropsWithChildren<{
- readonly methodName: string;
- readonly overloads: ReactNode[];
-}>) {
- const [hash, setHash] = useState(() => (typeof window === 'undefined' ? '' : window.location.hash));
- const hashChangeHandler = useCallback(() => {
- setHash(window.location.hash);
- }, []);
- const [overloadIndex, setOverloadIndex] = useState(1);
- const overloadedNode = overloads[overloadIndex - 1]!;
- const menu = useMenuState({ gutter: 8, sameWidth: true, fitViewport: true });
-
- useEffect(() => {
- window.addEventListener('hashchange', hashChangeHandler);
- return () => {
- window.removeEventListener('hashchange', hashChangeHandler);
- };
- });
-
- useEffect(() => {
- if (hash) {
- const elementId = hash.replace('#', '');
- const [name, idx] = elementId.split(':');
- if (name && methodName === name) {
- if (idx) {
- const hashOverload = Number.parseInt(idx, 10);
- const resolvedOverload = Math.max(Math.min(hashOverload, overloads.length), 1);
- setOverloadIndex(Number.isNaN(resolvedOverload) ? 1 : resolvedOverload);
- }
-
- const element = document.querySelector(`[id^='${name}']`);
- if (element) {
- element.scrollIntoView({ behavior: 'smooth' });
- }
- }
- }
- }, [hash, methodName, overloads.length]);
-
- const menuItems = useMemo(
- () =>
- overloads.map((_, idx) => (
-
- )),
- [overloads],
- );
-
- return (
-
-
-
-
-
- {`Overload ${overloadIndex}`}
- {` of ${overloads.length}`}
-
-
-
-
-
- {children}
- {overloadedNode}
-
- );
-}
diff --git a/apps/website/src/components/PackageSelect.tsx b/apps/website/src/components/PackageSelect.tsx
deleted file mode 100644
index bae2989ff654..000000000000
--- a/apps/website/src/components/PackageSelect.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-'use client';
-
-import { VscChevronDown } from '@react-icons/all-files/vsc/VscChevronDown';
-import { VscPackage } from '@react-icons/all-files/vsc/VscPackage';
-import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
-import Link from 'next/link';
-import { usePathname } from 'next/navigation';
-import { useMemo } from 'react';
-import { PACKAGES } from '~/util/constants';
-
-export default function PackageSelect() {
- const pathname = usePathname();
- const packageName = pathname?.split('/').slice(3, 4)[0];
-
- const packageMenu = useMenuState({
- gutter: 8,
- sameWidth: true,
- fitViewport: true,
- });
-
- const packageMenuItems = useMemo(
- () =>
- PACKAGES.map((pkg, idx) => (
-
-
-
- )),
- [packageMenu],
- );
-
- return (
- <>
-
-
-
-
- {packageName}
-
-
-
-
-
- >
- );
-}
diff --git a/apps/website/src/components/Panel.tsx b/apps/website/src/components/Panel.tsx
deleted file mode 100644
index 50e6a0ed835e..000000000000
--- a/apps/website/src/components/Panel.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import type { PropsWithChildren } from 'react';
-
-export function Panel({ children }: PropsWithChildren) {
- return (
- <>
- {children}
-
- >
- );
-}
diff --git a/apps/website/src/components/ParameterNode.tsx b/apps/website/src/components/ParameterNode.tsx
new file mode 100644
index 000000000000..02f35ec29b83
--- /dev/null
+++ b/apps/website/src/components/ParameterNode.tsx
@@ -0,0 +1,49 @@
+import { LinkIcon } from 'lucide-react';
+import Link from 'next/link';
+import { Fragment } from 'react';
+import { Badges } from './Badges';
+import { DocNode } from './DocNode';
+import { ExcerptNode } from './ExcerptNode';
+
+export async function ParameterNode({
+ description = false,
+ node,
+ version,
+}: {
+ readonly description?: boolean;
+ readonly node: any;
+ readonly version: string;
+}) {
+ return (
+
+ {node.map((parameter: any, idx: number) => {
+ return (
+
+
+
+ {description ? (
+
+
+
+ ) : null}
+ {description ? : null}
+ {parameter.name}
+ {parameter.isOptional ? '?' : ''}:
+
+ {description && parameter.description?.length ? (
+
+
+
+ ) : null}
+
+
+ );
+ })}
+ {description ? (
+
+ ) : null}
+
+ );
+}
diff --git a/apps/website/src/components/ParameterTable.tsx b/apps/website/src/components/ParameterTable.tsx
deleted file mode 100644
index c10c4791bef8..000000000000
--- a/apps/website/src/components/ParameterTable.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { ApiDocumentedItem, ApiParameterListMixin } from '@discordjs/api-extractor-model';
-import { useMemo } from 'react';
-import { resolveParameters } from '~/util/model';
-import { ExcerptText } from './ExcerptText';
-import { Table } from './Table';
-import { TSDoc } from './documentation/tsdoc/TSDoc';
-
-const columnStyles = {
- Name: 'font-mono whitespace-nowrap',
- Type: 'font-mono whitespace-pre-wrap break-normal',
-};
-
-export function ParameterTable({ item }: { readonly item: ApiDocumentedItem & ApiParameterListMixin }) {
- const params = resolveParameters(item);
-
- const rows = useMemo(
- () =>
- params.map((param) => ({
- Name: param.isRest ? `...${param.name}` : param.name,
- Type: ,
- Optional: param.isOptional ? 'Yes' : 'No',
- Description: param.description ? : 'None',
- })),
- [item, params],
- );
-
- return (
-
- );
-}
diff --git a/apps/website/src/components/Property.tsx b/apps/website/src/components/Property.tsx
deleted file mode 100644
index 242d7dad5709..000000000000
--- a/apps/website/src/components/Property.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import type {
- ApiDeclaredItem,
- ApiItemContainerMixin,
- ApiProperty,
- ApiPropertySignature,
-} from '@discordjs/api-extractor-model';
-import type { PropsWithChildren } from 'react';
-import { Badges } from './Badges';
-import { CodeHeading } from './CodeHeading';
-import { ExcerptText } from './ExcerptText';
-import { InheritanceText } from './InheritanceText';
-import { TSDoc } from './documentation/tsdoc/TSDoc';
-
-export function Property({
- item,
- children,
- inheritedFrom,
-}: PropsWithChildren<{
- readonly inheritedFrom?: (ApiDeclaredItem & ApiItemContainerMixin) | undefined;
- readonly item: ApiProperty | ApiPropertySignature;
-}>) {
- const hasSummary = Boolean(item.tsdocComment?.summarySection);
-
- return (
-
-
-
-
- {`${item.displayName}${item.isOptional ? '?' : ''}`}
- :
- {item.propertyTypeExcerpt.text ? (
-
- ) : null}
-
-
- {hasSummary || inheritedFrom ? (
-
- {item.tsdocComment ? : null}
- {inheritedFrom ? : null}
- {children}
-
- ) : null}
-
- );
-}
diff --git a/apps/website/src/components/PropertyList.tsx b/apps/website/src/components/PropertyList.tsx
deleted file mode 100644
index 8e922513ace0..000000000000
--- a/apps/website/src/components/PropertyList.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import type {
- ApiDeclaredItem,
- ApiItem,
- ApiItemContainerMixin,
- ApiProperty,
- ApiPropertySignature,
-} from '@discordjs/api-extractor-model';
-import { ApiItemKind } from '@discordjs/api-extractor-model';
-import { Fragment, useMemo } from 'react';
-import { resolveMembers } from '~/util/members';
-import { Property } from './Property';
-
-export function isPropertyLike(item: ApiItem): item is ApiProperty | ApiPropertySignature {
- return item.kind === ApiItemKind.Property || item.kind === ApiItemKind.PropertySignature;
-}
-
-export function PropertyList({ item }: { readonly item: ApiItemContainerMixin }) {
- const members = resolveMembers(item, isPropertyLike);
-
- const propertyItems = useMemo(
- () =>
- members.map((prop, idx) => {
- return (
-
-
-
-
- );
- }),
- [members],
- );
-
- return {propertyItems}
;
-}
diff --git a/apps/website/src/components/PropertyNode.tsx b/apps/website/src/components/PropertyNode.tsx
new file mode 100644
index 000000000000..60b023be1302
--- /dev/null
+++ b/apps/website/src/components/PropertyNode.tsx
@@ -0,0 +1,98 @@
+import { VscSymbolProperty } from '@react-icons/all-files/vsc/VscSymbolProperty';
+import { ChevronDown, ChevronUp, Code2, LinkIcon } from 'lucide-react';
+import Link from 'next/link';
+import { Fragment } from 'react';
+import { Badges } from './Badges';
+import { DeprecatedNode } from './DeprecatedNode';
+import { ExcerptNode } from './ExcerptNode';
+import { InheritedFromNode } from './InheritedFromNode';
+import { SeeNode } from './SeeNode';
+import { SummaryNode } from './SummaryNode';
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/Collapsible';
+
+export async function PropertyNode({
+ node,
+ packageName,
+ version,
+}: {
+ readonly node: any;
+ readonly packageName: string;
+ readonly version: string;
+}) {
+ return (
+
+
+
+
+ Properties
+
+
+
+
+
+
+
+ {node.map((property: any, idx: number) => {
+ return (
+
+
+
+
+
+
+
+
+
+ {property.displayName}
+ {property.isOptional ? '?' : ''} :
+
+
+
+
+
+
+
+
+ {property.summary?.deprecatedBlock.length ? (
+
+ ) : null}
+
+ {property.summary?.summarySection.length ? (
+
+ ) : null}
+
+ {property.inheritedFrom ? (
+
+ ) : null}
+
+ {property.summary?.seeBlocks.length ? (
+
+ ) : null}
+
+
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/apps/website/src/components/ReturnNode.tsx b/apps/website/src/components/ReturnNode.tsx
new file mode 100644
index 000000000000..2a18948654eb
--- /dev/null
+++ b/apps/website/src/components/ReturnNode.tsx
@@ -0,0 +1,17 @@
+import { DocNode } from './DocNode';
+
+export async function ReturnNode({
+ padding = false,
+ node,
+ version,
+}: {
+ readonly node: any;
+ readonly padding?: boolean;
+ readonly version: string;
+}) {
+ return (
+
+ Returns:
+
+ );
+}
diff --git a/apps/website/src/components/Scrollbars.tsx b/apps/website/src/components/Scrollbars.tsx
deleted file mode 100644
index 9e76b4923726..000000000000
--- a/apps/website/src/components/Scrollbars.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-'use client';
-
-import type { ScrollbarProps } from 'react-custom-scrollbars-2';
-import { Scrollbars as ReactScrollbars2 } from 'react-custom-scrollbars-2';
-
-export function Scrollbars(props: ScrollbarProps) {
- return ;
-}
diff --git a/apps/website/src/components/Section.tsx b/apps/website/src/components/Section.tsx
deleted file mode 100644
index 722344864a9a..000000000000
--- a/apps/website/src/components/Section.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-'use client';
-
-import { Section as DJSSection, type SectionOptions } from '@discordjs/ui';
-import type { PropsWithChildren } from 'react';
-
-export function Section(options: PropsWithChildren) {
- return ;
-}
diff --git a/apps/website/src/components/SeeNode.tsx b/apps/website/src/components/SeeNode.tsx
new file mode 100644
index 000000000000..4c8565395155
--- /dev/null
+++ b/apps/website/src/components/SeeNode.tsx
@@ -0,0 +1,17 @@
+import { DocNode } from './DocNode';
+
+export async function SeeNode({
+ padding = false,
+ node,
+ version,
+}: {
+ readonly node: any;
+ readonly padding?: boolean;
+ readonly version: string;
+}) {
+ return (
+
+ See also:
+
+ );
+}
diff --git a/apps/website/src/components/Sidebar.tsx b/apps/website/src/components/Sidebar.tsx
deleted file mode 100644
index b81fa250f2fb..000000000000
--- a/apps/website/src/components/Sidebar.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-'use client';
-
-import type { ApiItemKind } from '@discordjs/api-extractor-model';
-import { VscSymbolClass } from '@react-icons/all-files/vsc/VscSymbolClass';
-import { VscSymbolEnum } from '@react-icons/all-files/vsc/VscSymbolEnum';
-import { VscSymbolInterface } from '@react-icons/all-files/vsc/VscSymbolInterface';
-import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
-import { VscSymbolVariable } from '@react-icons/all-files/vsc/VscSymbolVariable';
-import { useSelectedLayoutSegment } from 'next/navigation';
-import { useMemo } from 'react';
-import { useNav } from '~/contexts/nav';
-import { ItemLink } from './ItemLink';
-import { Section } from './Section';
-
-export interface SidebarSectionItemData {
- href: string;
- kind: ApiItemKind;
- name: string;
- overloadIndex?: number | undefined;
-}
-
-interface GroupedMembers {
- Classes: SidebarSectionItemData[];
- Enums: SidebarSectionItemData[];
- Functions: SidebarSectionItemData[];
- Interfaces: SidebarSectionItemData[];
- Types: SidebarSectionItemData[];
- Variables: SidebarSectionItemData[];
-}
-
-function groupMembers(members: readonly SidebarSectionItemData[]): GroupedMembers {
- const Classes: SidebarSectionItemData[] = [];
- const Enums: SidebarSectionItemData[] = [];
- const Interfaces: SidebarSectionItemData[] = [];
- const Types: SidebarSectionItemData[] = [];
- const Variables: SidebarSectionItemData[] = [];
- const Functions: SidebarSectionItemData[] = [];
-
- for (const member of members) {
- switch (member.kind) {
- case 'Class':
- Classes.push(member);
- break;
- case 'Enum':
- Enums.push(member);
- break;
- case 'Interface':
- Interfaces.push(member);
- break;
- case 'TypeAlias':
- Types.push(member);
- break;
- case 'Variable':
- Variables.push(member);
- break;
- case 'Function':
- Functions.push(member);
- break;
- default:
- break;
- }
- }
-
- return { Classes, Functions, Enums, Interfaces, Types, Variables };
-}
-
-function resolveIcon(item: string) {
- switch (item) {
- case 'Classes':
- return ;
- case 'Enums':
- return ;
- case 'Interfaces':
- return ;
- case 'Types':
- case 'Variables':
- return ;
- default:
- return ;
- }
-}
-
-export function Sidebar({ members }: { readonly members: SidebarSectionItemData[] }) {
- const segment = useSelectedLayoutSegment();
- const { setOpened } = useNav();
-
- const groupItems = useMemo(() => groupMembers(members), [members]);
-
- return (
-
- {(Object.keys(groupItems) as (keyof GroupedMembers)[])
- .filter((group) => groupItems[group].length)
- .map((group, idx) => (
-
- {groupItems[group].map((member, index) => (
- setOpened(false)}
- title={member.name}
- >
-
- {member.name}
- {member.overloadIndex && member.overloadIndex > 1 ? (
- {member.overloadIndex}
- ) : null}
-
-
- ))}
-
- ))}
-
- );
-}
diff --git a/apps/website/src/components/SignatureText.tsx b/apps/website/src/components/SignatureText.tsx
deleted file mode 100644
index e381488b9c40..000000000000
--- a/apps/website/src/components/SignatureText.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import type { ApiPackage, Excerpt } from '@discordjs/api-extractor-model';
-import { ExcerptText } from './ExcerptText';
-
-export function SignatureText({ excerpt, apiPackage }: { readonly apiPackage: ApiPackage; readonly excerpt: Excerpt }) {
- return (
-
-
-
- );
-}
diff --git a/apps/website/src/components/SummaryNode.tsx b/apps/website/src/components/SummaryNode.tsx
new file mode 100644
index 000000000000..c0c2bffc9c3c
--- /dev/null
+++ b/apps/website/src/components/SummaryNode.tsx
@@ -0,0 +1,17 @@
+import { DocNode } from './DocNode';
+
+export async function SummaryNode({
+ padding = false,
+ node,
+ version,
+}: {
+ readonly node: any;
+ readonly padding?: boolean;
+ readonly version: string;
+}) {
+ return (
+
+
+
+ );
+}
diff --git a/apps/website/src/components/SyntaxHighlighter.tsx b/apps/website/src/components/SyntaxHighlighter.tsx
index 73f9251dc922..7d020385c024 100644
--- a/apps/website/src/components/SyntaxHighlighter.tsx
+++ b/apps/website/src/components/SyntaxHighlighter.tsx
@@ -1,14 +1,33 @@
-import { Code } from 'bright';
+import { getHighlighterCore } from 'shiki/core';
+import getWasm from 'shiki/wasm';
+
+const highlighter = await getHighlighterCore({
+ themes: [import('shiki/themes/github-light.mjs'), import('shiki/themes/github-dark-dimmed.mjs')],
+ langs: [import('shiki/langs/typescript.mjs'), import('shiki/langs/javascript.mjs')],
+ loadWasm: getWasm,
+});
+
+export async function SyntaxHighlighter({
+ lang,
+ code,
+ className = '',
+}: {
+ readonly className?: string;
+ readonly code: string;
+ readonly lang: string;
+}) {
+ const codeHTML = highlighter.codeToHtml(code.trim(), {
+ lang,
+ themes: {
+ light: 'github-light',
+ dark: 'github-dark-dimmed',
+ },
+ });
-export async function SyntaxHighlighter(props: typeof Code) {
return (
<>
-
-
-
-
-
-
+ {/* eslint-disable-next-line react/no-danger */}
+
>
);
}
diff --git a/apps/website/src/components/Table.tsx b/apps/website/src/components/Table.tsx
deleted file mode 100644
index 1929d1fe40c3..000000000000
--- a/apps/website/src/components/Table.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-'use client';
-
-import { useMemo, type ReactNode } from 'react';
-
-export function Table({
- rows,
- columns,
- columnStyles,
-}: {
- readonly columnStyles?: Record;
- readonly columns: string[];
- readonly rows: Record[];
-}) {
- const cols = useMemo(
- () =>
- columns.map((column, idx) => (
-
- {column}
- |
- )),
- [columns],
- );
-
- const data = useMemo(
- () =>
- rows.map((row, idx) => (
-
- {Object.entries(row).map(([colName, val], index) => (
-
- {val}
- |
- ))}
-
- )),
- [columnStyles, rows],
- );
-
- return (
-
- );
-}
diff --git a/apps/website/src/components/TableOfContentItems.tsx b/apps/website/src/components/TableOfContentItems.tsx
deleted file mode 100644
index a1e33316085f..000000000000
--- a/apps/website/src/components/TableOfContentItems.tsx
+++ /dev/null
@@ -1,161 +0,0 @@
-'use client';
-
-import { VscListSelection } from '@react-icons/all-files/vsc/VscListSelection';
-import { VscSymbolEvent } from '@react-icons/all-files/vsc/VscSymbolEvent';
-import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
-import { VscSymbolProperty } from '@react-icons/all-files/vsc/VscSymbolProperty';
-import { useMemo } from 'react';
-
-export interface TableOfContentsSerializedMethod {
- kind: 'Method' | 'MethodSignature';
- name: string;
- overloadIndex?: number;
-}
-
-export interface TableOfContentsSerializedProperty {
- kind: 'Property' | 'PropertySignature';
- name: string;
-}
-
-export interface TableOfContentsSerializedEvent {
- kind: 'Event';
- name: string;
-}
-
-export type TableOfContentsSerialized =
- | TableOfContentsSerializedEvent
- | TableOfContentsSerializedMethod
- | TableOfContentsSerializedProperty;
-
-export interface TableOfContentsItemProps {
- readonly serializedMembers: TableOfContentsSerialized[];
-}
-
-export function TableOfContentsPropertyItem({ property }: { readonly property: TableOfContentsSerializedProperty }) {
- return (
-
- {property.name}
-
- );
-}
-
-export function TableOfContentsMethodItem({ method }: { readonly method: TableOfContentsSerializedMethod }) {
- if (method.overloadIndex && method.overloadIndex > 1) {
- return null;
- }
-
- const key = `${method.name}${method.overloadIndex && method.overloadIndex > 1 ? `:${method.overloadIndex}` : ''}`;
-
- return (
-
- {method.name}
- {method.overloadIndex && method.overloadIndex > 1 ? (
- {method.overloadIndex}
- ) : null}
-
- );
-}
-
-export function TableOfContentsEventItem({ event }: { readonly event: TableOfContentsSerializedEvent }) {
- return (
-
- {event.name}
-
- );
-}
-
-export function TableOfContentItems({ serializedMembers }: TableOfContentsItemProps) {
- const propertyItems = useMemo(
- () =>
- serializedMembers
- .filter(
- (member): member is TableOfContentsSerializedProperty =>
- member.kind === 'Property' || member.kind === 'PropertySignature',
- )
- .map((prop, idx) => ),
- [serializedMembers],
- );
-
- const methodItems = useMemo(
- () =>
- serializedMembers
- .filter(
- (member): member is TableOfContentsSerializedMethod =>
- member.kind === 'Method' || member.kind === 'MethodSignature',
- )
- .map((member, idx) => (
-
- )),
- [serializedMembers],
- );
-
- const eventItems = useMemo(
- () =>
- serializedMembers
- .filter((member): member is TableOfContentsSerializedEvent => member.kind === 'Event')
- .map((event, idx) => ),
- [serializedMembers],
- );
-
- return (
-
-
-
- Contents
-
-
- {eventItems.length ? (
-
- ) : null}
- {propertyItems.length ? (
-
- ) : null}
- {methodItems.length ? (
-
- ) : null}
-
-
- );
-}
diff --git a/apps/website/src/components/ThemeSwitcher.tsx b/apps/website/src/components/ThemeSwitcher.tsx
deleted file mode 100644
index 38acea06884b..000000000000
--- a/apps/website/src/components/ThemeSwitcher.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-'use client';
-
-import { VscColorMode } from '@react-icons/all-files/vsc/VscColorMode';
-import { Button } from 'ariakit/button';
-import { useTheme } from 'next-themes';
-
-export default function ThemeSwitcher() {
- const { resolvedTheme, setTheme } = useTheme();
- const toggleTheme = () => setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
-
- return (
-
- );
-}
diff --git a/apps/website/src/components/TypeParamTable.tsx b/apps/website/src/components/TypeParamTable.tsx
deleted file mode 100644
index a16f79d07ee0..000000000000
--- a/apps/website/src/components/TypeParamTable.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import type { ApiTypeParameterListMixin } from '@discordjs/api-extractor-model';
-import { useMemo } from 'react';
-import { ExcerptText } from './ExcerptText';
-import { Table } from './Table';
-import { TSDoc } from './documentation/tsdoc/TSDoc';
-
-const rowElements = {
- Name: 'font-mono whitespace-nowrap',
- Constraints: 'font-mono whitespace-pre break-normal',
- Default: 'font-mono whitespace-pre break-normal',
-};
-
-export function TypeParamTable({ item }: { readonly item: ApiTypeParameterListMixin }) {
- const rows = useMemo(
- () =>
- item.typeParameters.map((typeParam) => ({
- Name: typeParam.name,
- Constraints: ,
- Optional: typeParam.isOptional ? 'Yes' : 'No',
- Default: ,
- Description: typeParam.tsdocTypeParamBlock ? (
-
- ) : (
- 'None'
- ),
- })),
- [item],
- );
-
- return (
-
- );
-}
diff --git a/apps/website/src/components/TypeParameterNode.tsx b/apps/website/src/components/TypeParameterNode.tsx
new file mode 100644
index 000000000000..1c0c55764891
--- /dev/null
+++ b/apps/website/src/components/TypeParameterNode.tsx
@@ -0,0 +1,67 @@
+import { LinkIcon } from 'lucide-react';
+import Link from 'next/link';
+import { Fragment } from 'react';
+import { Badges } from './Badges';
+import { DocNode } from './DocNode';
+import { ExcerptNode } from './ExcerptNode';
+
+export async function TypeParameterNode({
+ description = false,
+ node,
+ version,
+}: {
+ readonly description?: boolean;
+ readonly node: any;
+ readonly version: string;
+}) {
+ return (
+
+ {node.map((typeParameter: any, idx: number) => {
+ return (
+
+
+
+ {description ? : null}
+
+ {description ? (
+
+
+
+ ) : null}
+ {typeParameter.name}
+ {typeParameter.isOptional ? '?' : ''}
+ {typeParameter.constraintsExcerpt.length ? (
+ <>
+ {' extends '}
+
+ >
+ ) : null}
+ {typeParameter.defaultExcerpt.length ? (
+ <>
+ {' = '}
+
+ >
+ ) : null}
+
+
+
+ {description && typeParameter.description?.length ? (
+
+
+
+ ) : null}
+
+
+ );
+ })}
+ {description ? (
+
+ ) : null}
+
+ );
+}
diff --git a/apps/website/src/components/UnionMember.tsx b/apps/website/src/components/UnionMember.tsx
new file mode 100644
index 000000000000..25f7989bdce5
--- /dev/null
+++ b/apps/website/src/components/UnionMember.tsx
@@ -0,0 +1,13 @@
+import { ExcerptNode } from './ExcerptNode';
+
+export async function UnionMember({ node, version }: { readonly node: any; readonly version: string }) {
+ return (
+
+
Union Members
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/VersionSelect.tsx b/apps/website/src/components/VersionSelect.tsx
deleted file mode 100644
index 72ed52318b7a..000000000000
--- a/apps/website/src/components/VersionSelect.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-'use client';
-
-import { VscChevronDown } from '@react-icons/all-files/vsc/VscChevronDown';
-import { VscVersions } from '@react-icons/all-files/vsc/VscVersions';
-import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
-import Link from 'next/link';
-import { usePathname } from 'next/navigation';
-import { useMemo } from 'react';
-import useSWR from 'swr';
-
-const isDev = process.env.NEXT_PUBLIC_LOCAL_DEV === 'true' ?? process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview';
-
-export default function VersionSelect({ versions }: { readonly versions: string[] }) {
- const pathname = usePathname();
- const packageName = pathname?.split('/').slice(3, 4)[0];
- const branchName = pathname?.split('/').slice(4, 5)[0];
-
- const { data } = useSWR(packageName ? `/api/${packageName}/versions` : null, {
- fallbackData: versions,
- });
-
- const versionMenu = useMenuState({
- gutter: 8,
- sameWidth: true,
- fitViewport: true,
- });
-
- const versionMenuItems = useMemo(
- () =>
- data?.map((item, idx) => (
-
-
-
- )) ?? [],
- [data, packageName, versionMenu],
- );
-
- return (
- <>
-
-
-
-
- >
- );
-}
diff --git a/apps/website/src/components/documentation/Documentation.tsx b/apps/website/src/components/documentation/Documentation.tsx
deleted file mode 100644
index ffddee132463..000000000000
--- a/apps/website/src/components/documentation/Documentation.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import type { PropsWithChildren } from 'react';
-
-/**
- * Layout parent of documentation pages.
- */
-export function Documentation({ children }: PropsWithChildren) {
- return {children}
;
-}
diff --git a/apps/website/src/components/documentation/Header.tsx b/apps/website/src/components/documentation/Header.tsx
deleted file mode 100644
index 96dc2f4cc3e4..000000000000
--- a/apps/website/src/components/documentation/Header.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { ApiItemKind } from '@discordjs/api-extractor-model';
-import { VscSymbolClass } from '@react-icons/all-files/vsc/VscSymbolClass';
-import { VscSymbolEnum } from '@react-icons/all-files/vsc/VscSymbolEnum';
-import { VscSymbolInterface } from '@react-icons/all-files/vsc/VscSymbolInterface';
-import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
-import { VscSymbolVariable } from '@react-icons/all-files/vsc/VscSymbolVariable';
-import type { PropsWithChildren } from 'react';
-import { SourceLink } from './SourceLink';
-
-function generateIcon(kind: ApiItemKind) {
- switch (kind) {
- case ApiItemKind.Class:
- return ;
- case ApiItemKind.Function:
- case ApiItemKind.Method:
- return ;
- case ApiItemKind.Enum:
- return ;
- case ApiItemKind.Interface:
- return ;
- case ApiItemKind.TypeAlias:
- case ApiItemKind.Variable:
- return ;
- default:
- return ;
- }
-}
-
-export function Header({
- kind,
- name,
- sourceURL,
- sourceLine,
-}: PropsWithChildren<{
- readonly kind: ApiItemKind;
- readonly name: string;
- readonly sourceLine?: number | undefined;
- readonly sourceURL?: string | undefined;
-}>) {
- return (
-
-
-
- {generateIcon(kind)}
- {name}
-
- {sourceURL ? : null}
-
-
- );
-}
diff --git a/apps/website/src/components/documentation/HierarchyText.tsx b/apps/website/src/components/documentation/HierarchyText.tsx
deleted file mode 100644
index 6bf9a5ec7767..000000000000
--- a/apps/website/src/components/documentation/HierarchyText.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import type { ApiClass, ApiInterface, Excerpt } from '@discordjs/api-extractor-model';
-import { ApiItemKind } from '@discordjs/api-extractor-model';
-import { ExcerptText } from '../ExcerptText';
-
-export function HierarchyText({
- item,
- type,
-}: {
- readonly item: ApiClass | ApiInterface;
- readonly type: 'Extends' | 'Implements';
-}) {
- if (
- (item.kind === ApiItemKind.Class &&
- (item as ApiClass).extendsType === undefined &&
- (item as ApiClass).implementsTypes.length === 0) ||
- (item.kind === ApiItemKind.Interface && !(item as ApiInterface).extendsTypes)
- ) {
- return null;
- }
-
- let excerpts: Excerpt[];
-
- if (item.kind === ApiItemKind.Class) {
- if (type === 'Implements') {
- if ((item as ApiClass).implementsTypes.length === 0) {
- return null;
- }
-
- excerpts = (item as ApiClass).implementsTypes.map((typeExcerpt) => typeExcerpt.excerpt);
- } else {
- if (!(item as ApiClass).extendsType) {
- return null;
- }
-
- excerpts = [(item as ApiClass).extendsType!.excerpt];
- }
- } else {
- if ((item as ApiInterface).extendsTypes.length === 0) {
- return null;
- }
-
- excerpts = (item as ApiInterface).extendsTypes.map((typeExcerpt) => typeExcerpt.excerpt);
- }
-
- return (
-
- {excerpts.map((excerpt, idx) => (
-
-
{type}
-
-
-
-
- ))}
-
- );
-}
diff --git a/apps/website/src/components/documentation/Members.tsx b/apps/website/src/components/documentation/Members.tsx
deleted file mode 100644
index 64a69a4028c6..000000000000
--- a/apps/website/src/components/documentation/Members.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import type { ApiDeclaredItem, ApiItemContainerMixin } from '@discordjs/api-extractor-model';
-import { EventsSection } from './section/EventsSection';
-import { MethodsSection } from './section/MethodsSection';
-import { PropertiesSection } from './section/PropertiesSection';
-import { hasEvents, hasProperties, hasMethods } from './util';
-
-export function Members({ item }: { readonly item: ApiDeclaredItem & ApiItemContainerMixin }) {
- return (
- <>
- {hasEvents(item) ? : null}
- {hasProperties(item) ? : null}
- {hasMethods(item) ? : null}
- >
- );
-}
diff --git a/apps/website/src/components/documentation/ObjectHeader.tsx b/apps/website/src/components/documentation/ObjectHeader.tsx
deleted file mode 100644
index 198e54ac252e..000000000000
--- a/apps/website/src/components/documentation/ObjectHeader.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import type { ApiDeclaredItem } from '@discordjs/api-extractor-model';
-import { SyntaxHighlighter } from '../SyntaxHighlighter';
-import { Header } from './Header';
-import { SummarySection } from './section/SummarySection';
-
-export interface ObjectHeaderProps {
- readonly item: ApiDeclaredItem;
-}
-
-export function ObjectHeader({ item }: ObjectHeaderProps) {
- return (
- <>
-
- {/* @ts-expect-error async component */}
-
-
- >
- );
-}
diff --git a/apps/website/src/components/documentation/SourceLink.tsx b/apps/website/src/components/documentation/SourceLink.tsx
deleted file mode 100644
index 9ecc9115e67e..000000000000
--- a/apps/website/src/components/documentation/SourceLink.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { VscFileCode } from '@react-icons/all-files/vsc/VscFileCode';
-
-export function SourceLink({
- className,
- sourceURL,
- sourceLine,
-}: {
- readonly className?: string | undefined;
- readonly sourceLine?: number | undefined;
- readonly sourceURL?: string | undefined;
-}) {
- return (
-
-
-
- );
-}
diff --git a/apps/website/src/components/documentation/section/ConstructorSection.tsx b/apps/website/src/components/documentation/section/ConstructorSection.tsx
deleted file mode 100644
index c06d9e7caf4a..000000000000
--- a/apps/website/src/components/documentation/section/ConstructorSection.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { ApiConstructor } from '@discordjs/api-extractor-model';
-import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
-import { CodeHeading } from '~/components/CodeHeading';
-import { ParameterTable } from '../../ParameterTable';
-import { TSDoc } from '../tsdoc/TSDoc';
-import { parametersString } from '../util';
-import { DocumentationSection } from './DocumentationSection';
-
-export function ConstructorSection({ item }: { readonly item: ApiConstructor }) {
- return (
- } padded title="Constructor">
-
-
{`constructor(${parametersString(item)})`}
- {item.tsdocComment ?
: null}
-
-
-
- );
-}
diff --git a/apps/website/src/components/documentation/section/DocumentationSection.tsx b/apps/website/src/components/documentation/section/DocumentationSection.tsx
deleted file mode 100644
index e56bd389bd37..000000000000
--- a/apps/website/src/components/documentation/section/DocumentationSection.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import type { SectionOptions } from '@discordjs/ui';
-import type { PropsWithChildren } from 'react';
-import { Section } from '../../Section';
-
-export function DocumentationSection(opts: PropsWithChildren) {
- const { children, separator, ...props } = opts;
-
- return (
-
- {children}
- {separator ? : null}
-
- );
-}
diff --git a/apps/website/src/components/documentation/section/EventsSection.tsx b/apps/website/src/components/documentation/section/EventsSection.tsx
deleted file mode 100644
index a91164c4eb21..000000000000
--- a/apps/website/src/components/documentation/section/EventsSection.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import {
- ApiItemKind,
- type ApiEvent,
- type ApiItem,
- type ApiItemContainerMixin,
- type ApiDeclaredItem,
-} from '@discordjs/api-extractor-model';
-import { VscSymbolEvent } from '@react-icons/all-files/vsc/VscSymbolEvent';
-import { Fragment, useMemo } from 'react';
-import { Event } from '~/components/model/Event';
-import { resolveMembers } from '~/util/members';
-import { DocumentationSection } from './DocumentationSection';
-
-function isEventLike(item: ApiItem): item is ApiEvent {
- return item.kind === ApiItemKind.Event;
-}
-
-export function EventsSection({ item }: { readonly item: ApiItemContainerMixin }) {
- const members = resolveMembers(item, isEventLike);
-
- const eventItems = useMemo(
- () =>
- members.map((event, idx) => {
- return (
-
-
-
-
- );
- }),
- [members],
- );
-
- return (
- } padded title="Events">
- {eventItems}
-
- );
-}
diff --git a/apps/website/src/components/documentation/section/MethodsSection.tsx b/apps/website/src/components/documentation/section/MethodsSection.tsx
deleted file mode 100644
index f68479d45487..000000000000
--- a/apps/website/src/components/documentation/section/MethodsSection.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import type {
- ApiDeclaredItem,
- ApiItem,
- ApiItemContainerMixin,
- ApiMethod,
- ApiMethodSignature,
-} from '@discordjs/api-extractor-model';
-import { ApiItemKind } from '@discordjs/api-extractor-model';
-import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
-import { useMemo, Fragment } from 'react';
-import { resolveMembers } from '~/util/members';
-import { Method } from '../../model/method/Method';
-import { DocumentationSection } from './DocumentationSection';
-
-function isMethodLike(item: ApiItem): item is ApiMethod | ApiMethodSignature {
- return (
- item.kind === ApiItemKind.Method ||
- (item.kind === ApiItemKind.MethodSignature && (item as ApiMethod).overloadIndex <= 1)
- );
-}
-
-export function MethodsSection({ item }: { readonly item: ApiItemContainerMixin }) {
- const members = resolveMembers(item, isMethodLike);
-
- const methodItems = useMemo(
- () =>
- members.map(({ item: method, inherited }) => (
- 1 ? `:${(method as ApiMethod).overloadIndex}` : ''
- }`}
- >
-
-
-
- )),
- [members],
- );
-
- return (
- } padded title="Methods">
- {methodItems}
-
- );
-}
diff --git a/apps/website/src/components/documentation/section/ParametersSection.tsx b/apps/website/src/components/documentation/section/ParametersSection.tsx
deleted file mode 100644
index 71f0d38326fb..000000000000
--- a/apps/website/src/components/documentation/section/ParametersSection.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { ApiDocumentedItem, ApiParameterListMixin } from '@discordjs/api-extractor-model';
-import { VscSymbolParameter } from '@react-icons/all-files/vsc/VscSymbolParameter';
-import { ParameterTable } from '../../ParameterTable';
-import { DocumentationSection } from './DocumentationSection';
-
-export function ParameterSection({ item }: { readonly item: ApiDocumentedItem & ApiParameterListMixin }) {
- return (
- } padded title="Parameters">
-
-
- );
-}
diff --git a/apps/website/src/components/documentation/section/PropertiesSection.tsx b/apps/website/src/components/documentation/section/PropertiesSection.tsx
deleted file mode 100644
index 999201b97394..000000000000
--- a/apps/website/src/components/documentation/section/PropertiesSection.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { ApiItemContainerMixin } from '@discordjs/api-extractor-model';
-import { VscSymbolProperty } from '@react-icons/all-files/vsc/VscSymbolProperty';
-import { PropertyList } from '../../PropertyList';
-import { DocumentationSection } from './DocumentationSection';
-
-export function PropertiesSection({ item }: { readonly item: ApiItemContainerMixin }) {
- return (
- } padded title="Properties">
-
-
- );
-}
diff --git a/apps/website/src/components/documentation/section/SummarySection.tsx b/apps/website/src/components/documentation/section/SummarySection.tsx
deleted file mode 100644
index c47287b6a977..000000000000
--- a/apps/website/src/components/documentation/section/SummarySection.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { ApiDeclaredItem } from '@discordjs/api-extractor-model';
-import { VscListSelection } from '@react-icons/all-files/vsc/VscListSelection';
-import { TSDoc } from '../tsdoc/TSDoc';
-import { DocumentationSection } from './DocumentationSection';
-
-export function SummarySection({ item }: { readonly item: ApiDeclaredItem }) {
- return (
- } padded separator title="Summary">
- {item.tsdocComment?.summarySection ? (
-
- ) : (
- No summary provided.
- )}
-
- );
-}
diff --git a/apps/website/src/components/documentation/section/TypeParametersSection.tsx b/apps/website/src/components/documentation/section/TypeParametersSection.tsx
deleted file mode 100644
index b93246b2313f..000000000000
--- a/apps/website/src/components/documentation/section/TypeParametersSection.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { ApiTypeParameterListMixin } from '@discordjs/api-extractor-model';
-import { VscSymbolParameter } from '@react-icons/all-files/vsc/VscSymbolParameter';
-import { TypeParamTable } from '../../TypeParamTable';
-import { DocumentationSection } from './DocumentationSection';
-
-export function TypeParameterSection({ item }: { readonly item: ApiTypeParameterListMixin }) {
- return (
- } padded title="Type Parameters">
-
-
- );
-}
diff --git a/apps/website/src/components/documentation/section/UnionMembersSection.tsx b/apps/website/src/components/documentation/section/UnionMembersSection.tsx
deleted file mode 100644
index 2a736c3de0a5..000000000000
--- a/apps/website/src/components/documentation/section/UnionMembersSection.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Excerpt, type ApiTypeAlias, type ExcerptToken } from '@discordjs/api-extractor-model';
-import { VscSymbolArray } from '@react-icons/all-files/vsc/VscSymbolArray';
-import { useMemo } from 'react';
-import { ExcerptText } from '~/components/ExcerptText';
-import { DocumentationSection } from './DocumentationSection';
-
-export type UnionMember = readonly ExcerptToken[];
-
-export function UnionMembersSection({
- item,
- members,
-}: {
- readonly item: ApiTypeAlias;
- readonly members: UnionMember[];
-}) {
- const unionMembers = useMemo(
- () =>
- members.map((member, idx) => (
-
-
-
-
-
- )),
- [item, members],
- );
-
- return (
- } padded title="Union Members">
- {unionMembers}
-
- );
-}
diff --git a/apps/website/src/components/documentation/tsdoc/BlockComment.tsx b/apps/website/src/components/documentation/tsdoc/BlockComment.tsx
deleted file mode 100644
index 8d2a055112a3..000000000000
--- a/apps/website/src/components/documentation/tsdoc/BlockComment.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Alert } from '@discordjs/ui';
-import type { PropsWithChildren } from 'react';
-
-export function Block({ children, title }: PropsWithChildren<{ readonly title: string }>) {
- return (
-
-
{title}
- {children}
-
- );
-}
-
-export function ExampleBlock({
- children,
- exampleIndex,
-}: PropsWithChildren<{
- readonly exampleIndex?: number | undefined;
-}>): JSX.Element {
- return {children};
-}
-
-export function DefaultValueBlock({ children }: PropsWithChildren): JSX.Element {
- return {children};
-}
-
-export function RemarksBlock({ children }: PropsWithChildren): JSX.Element {
- return {children};
-}
-
-export function DeprecatedBlock({ children }: PropsWithChildren): JSX.Element {
- return (
-
- {children}
-
- );
-}
-
-export function SeeBlock({ children }: PropsWithChildren): JSX.Element {
- return {children};
-}
-
-export function ReturnsBlock({ children }: PropsWithChildren): JSX.Element {
- return {children};
-}
diff --git a/apps/website/src/components/documentation/tsdoc/TSDoc.tsx b/apps/website/src/components/documentation/tsdoc/TSDoc.tsx
deleted file mode 100644
index 8f5b007a4873..000000000000
--- a/apps/website/src/components/documentation/tsdoc/TSDoc.tsx
+++ /dev/null
@@ -1,178 +0,0 @@
-import type { ApiItem } from '@discordjs/api-extractor-model';
-import type { DocComment, DocFencedCode, DocLinkTag, DocNode, DocNodeContainer, DocPlainText } from '@microsoft/tsdoc';
-import { DocNodeKind, StandardTags } from '@microsoft/tsdoc';
-import type { Route } from 'next';
-import Link from 'next/link';
-import { Fragment, useCallback, type ReactNode } from 'react';
-import { DocumentationLink } from '~/components/DocumentationLink';
-import { BuiltinDocumentationLinks } from '~/util/builtinDocumentationLinks';
-import { DISCORD_API_TYPES_DOCS_URL } from '~/util/constants';
-import { ItemLink } from '../../ItemLink';
-import { SyntaxHighlighter } from '../../SyntaxHighlighter';
-import { resolveCanonicalReference, resolveItemURI } from '../util';
-import { DefaultValueBlock, DeprecatedBlock, ExampleBlock, RemarksBlock, ReturnsBlock, SeeBlock } from './BlockComment';
-
-export function TSDoc({ item, tsdoc }: { readonly item: ApiItem; readonly tsdoc: DocNode }): JSX.Element {
- const createNode = useCallback(
- (tsdoc: DocNode, idx?: number): ReactNode => {
- switch (tsdoc.kind) {
- case DocNodeKind.PlainText:
- return (
-
- {(tsdoc as DocPlainText).text}
-
- );
- case DocNodeKind.Section:
- case DocNodeKind.Paragraph:
- return (
-
- {(tsdoc as DocNodeContainer).nodes.map((node, idx) => createNode(node, idx))}
-
- );
- case DocNodeKind.SoftBreak:
- return ;
- case DocNodeKind.LinkTag: {
- const { codeDestination, urlDestination, linkText } = tsdoc as DocLinkTag;
- if (codeDestination) {
- if (
- !codeDestination.importPath &&
- !codeDestination.packageName &&
- codeDestination.memberReferences.length === 1 &&
- codeDestination.memberReferences[0]!.memberIdentifier &&
- codeDestination.memberReferences[0]!.memberIdentifier.identifier in BuiltinDocumentationLinks
- ) {
- const typeName = codeDestination.memberReferences[0]!.memberIdentifier.identifier;
- const href = BuiltinDocumentationLinks[typeName as keyof typeof BuiltinDocumentationLinks];
- return (
-
- {typeName}
-
- );
- }
-
- const declarationReference = item.getAssociatedModel()?.resolveDeclarationReference(codeDestination, item);
- const foundItem = declarationReference?.resolvedApiItem;
- const resolved = resolveCanonicalReference(codeDestination, item.getAssociatedPackage());
-
- if (!foundItem && !resolved) return null;
-
- if (resolved && resolved.package === 'discord-api-types') {
- const { displayName, kind, members, containerKey } = resolved.item;
- let href = DISCORD_API_TYPES_DOCS_URL;
-
- // dapi-types doesn't have routes for class members
- // so we can assume this member is for an enum
- if (kind === 'enum' && members?.[0]) {
- href += `/enum/${displayName}#${members[0].displayName}`;
- } else if (kind === 'type' || kind === 'var') {
- href += `#${displayName}`;
- } else {
- href += `/${kind}/${displayName}`;
- }
-
- return (
-
- {displayName}
- {members?.map((member) => `.${member.displayName}`).join('') ?? ''}
-
- );
- }
-
- return (
-
- {linkText ?? foundItem?.displayName ?? resolved!.item.displayName}
-
- );
- }
-
- if (urlDestination) {
- return (
-
- {linkText ?? urlDestination}
-
- );
- }
-
- return null;
- }
-
- case DocNodeKind.CodeSpan: {
- const { code } = tsdoc as DocFencedCode;
- return (
-
- {code}
-
- );
- }
-
- case DocNodeKind.FencedCode: {
- const { language, code } = tsdoc as DocFencedCode;
- // @ts-expect-error async component
- return ;
- }
-
- case DocNodeKind.Comment: {
- const comment = tsdoc as DocComment;
-
- const exampleBlocks = comment.customBlocks.filter(
- (block) => block.blockTag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase,
- );
-
- const defaultValueBlock = comment.customBlocks.find(
- (block) => block.blockTag.tagName.toUpperCase() === StandardTags.defaultValue.tagNameWithUpperCase,
- );
-
- return (
-
- {comment.deprecatedBlock ? (
- {createNode(comment.deprecatedBlock.content)}
- ) : null}
- {comment.summarySection ? createNode(comment.summarySection) : null}
- {comment.remarksBlock ? {createNode(comment.remarksBlock.content)} : null}
- {defaultValueBlock ? (
- {createNode(defaultValueBlock.content)}
- ) : null}
- {comment.returnsBlock ? {createNode(comment.returnsBlock.content)} : null}
- {exampleBlocks.length
- ? exampleBlocks.map((block, idx) => {createNode(block.content)})
- : null}
- {comment.seeBlocks.length ? (
- {comment.seeBlocks.map((seeBlock, idx) => createNode(seeBlock.content, idx))}
- ) : null}
-
- );
- }
-
- default:
- // console.log(`Captured unknown node kind: ${node.kind}`);
- return null;
- }
- },
- [item],
- );
-
- return (
- <>
- {tsdoc.kind === 'Paragraph' || tsdoc.kind === 'Section' ? (
- <>{(tsdoc as DocNodeContainer).nodes.map((node, idx) => createNode(node, idx))}>
- ) : (
- createNode(tsdoc)
- )}
- >
- );
-}
diff --git a/apps/website/src/components/documentation/util.ts b/apps/website/src/components/documentation/util.ts
deleted file mode 100644
index 165972f2dfb6..000000000000
--- a/apps/website/src/components/documentation/util.ts
+++ /dev/null
@@ -1,173 +0,0 @@
-import { ApiItemKind, Meaning } from '@discordjs/api-extractor-model';
-import type {
- ApiItem,
- ApiItemContainerMixin,
- ApiMethod,
- ApiMethodSignature,
- ApiProperty,
- ApiPropertySignature,
- ApiDocumentedItem,
- ApiParameterListMixin,
- ApiEvent,
- ApiPackage,
-} from '@discordjs/api-extractor-model';
-import type { DocDeclarationReference } from '@microsoft/tsdoc';
-import { SelectorKind } from '@microsoft/tsdoc';
-import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference';
-import { METHOD_SEPARATOR, OVERLOAD_SEPARATOR } from '~/util/constants';
-import { resolveMembers } from '~/util/members';
-import { resolveParameters } from '~/util/model';
-import type { TableOfContentsSerialized } from '../TableOfContentItems';
-
-export interface ApiItemLike {
- containerKey?: string;
- displayName: string;
- kind: string;
- members?: readonly ApiItemLike[];
- parent?: ApiItemLike | undefined;
-}
-
-interface ResolvedCanonicalReference {
- item: ApiItemLike;
- package: string | undefined;
- version: string | undefined;
-}
-
-const kindToMeaning = new Map([
- [ApiItemKind.CallSignature, Meaning.CallSignature],
- [ApiItemKind.Class, Meaning.Class],
- [ApiItemKind.ConstructSignature, Meaning.ConstructSignature],
- [ApiItemKind.Constructor, Meaning.Constructor],
- [ApiItemKind.Enum, Meaning.Enum],
- [ApiItemKind.Event, Meaning.Event],
- [ApiItemKind.Function, Meaning.Function],
- [ApiItemKind.IndexSignature, Meaning.IndexSignature],
- [ApiItemKind.Interface, Meaning.Interface],
- [ApiItemKind.Property, Meaning.Member],
- [ApiItemKind.Namespace, Meaning.Namespace],
- [ApiItemKind.None, Meaning.ComplexType],
- [ApiItemKind.TypeAlias, Meaning.TypeAlias],
- [ApiItemKind.Variable, Meaning.Variable],
-]);
-
-export function hasProperties(item: ApiItemContainerMixin) {
- return resolveMembers(item, memberPredicate).some(
- ({ item: member }) => member.kind === ApiItemKind.Property || member.kind === ApiItemKind.PropertySignature,
- );
-}
-
-export function hasMethods(item: ApiItemContainerMixin) {
- return resolveMembers(item, memberPredicate).some(
- ({ item: member }) => member.kind === ApiItemKind.Method || member.kind === ApiItemKind.MethodSignature,
- );
-}
-
-export function hasEvents(item: ApiItemContainerMixin) {
- return resolveMembers(item, memberPredicate).some(({ item: member }) => member.kind === ApiItemKind.Event);
-}
-
-export function resolveItemURI(item: ApiItemLike): string {
- return !item.parent || item.parent.kind === ApiItemKind.EntryPoint
- ? `${item.displayName}${OVERLOAD_SEPARATOR}${item.kind}`
- : `${item.parent.displayName}${OVERLOAD_SEPARATOR}${item.parent.kind}${METHOD_SEPARATOR}${item.displayName}`;
-}
-
-export function resolveCanonicalReference(
- canonicalReference: DeclarationReference | DocDeclarationReference,
- apiPackage: ApiPackage | undefined,
-): ResolvedCanonicalReference | null {
- if (
- 'source' in canonicalReference &&
- canonicalReference.source &&
- 'packageName' in canonicalReference.source &&
- canonicalReference.symbol?.componentPath &&
- canonicalReference.symbol.meaning
- )
- return {
- package: canonicalReference.source.unscopedPackageName,
- item: {
- kind: mapMeaningToKind(canonicalReference.symbol.meaning as unknown as Meaning),
- displayName: canonicalReference.symbol.componentPath.component.toString(),
- containerKey: `|${
- canonicalReference.symbol.meaning
- }|${canonicalReference.symbol.componentPath.component.toString()}`,
- },
- // eslint-disable-next-line unicorn/better-regex
- version: apiPackage?.dependencies?.[canonicalReference.source.packageName]?.replace(/[~^]/, ''),
- };
- else if (
- 'memberReferences' in canonicalReference &&
- canonicalReference.memberReferences.length &&
- canonicalReference.memberReferences[0]?.memberIdentifier &&
- canonicalReference.memberReferences[0]?.selector?.selectorKind === SelectorKind.System
- ) {
- const member = canonicalReference.memberReferences[0]!;
- return {
- package: canonicalReference.packageName?.replace('@discordjs/', ''),
- item: {
- kind: member.selector!.selector,
- displayName: member.memberIdentifier!.identifier,
- containerKey: `|${member.selector!.selector}|${member.memberIdentifier!.identifier}`,
- members: canonicalReference.memberReferences
- .slice(1)
- .map((member) => ({ kind: member.kind, displayName: member.memberIdentifier!.identifier! })),
- },
- // eslint-disable-next-line unicorn/better-regex
- version: apiPackage?.dependencies?.[canonicalReference.packageName ?? '']?.replace(/[~^]/, ''),
- };
- }
-
- return null;
-}
-
-export function mapMeaningToKind(meaning: Meaning): ApiItemKind {
- return [...kindToMeaning.entries()].find((mapping) => mapping[1] === meaning)?.[0] ?? ApiItemKind.None;
-}
-
-export function mapKindToMeaning(kind: ApiItemKind): Meaning {
- return kindToMeaning.get(kind) ?? Meaning.Variable;
-}
-
-export function memberPredicate(
- item: ApiItem,
-): item is ApiEvent | ApiMethod | ApiMethodSignature | ApiProperty | ApiPropertySignature {
- return (
- item.kind === ApiItemKind.Property ||
- item.kind === ApiItemKind.PropertySignature ||
- item.kind === ApiItemKind.Method ||
- item.kind === ApiItemKind.MethodSignature ||
- item.kind === ApiItemKind.Event
- );
-}
-
-export function serializeMembers(clazz: ApiItemContainerMixin): TableOfContentsSerialized[] {
- return resolveMembers(clazz, memberPredicate).map(({ item: member }) => {
- if (member.kind === 'Method' || member.kind === 'MethodSignature') {
- return {
- kind: member.kind as 'Method' | 'MethodSignature',
- name: member.displayName,
- overloadIndex: (member as ApiMethod | ApiMethodSignature).overloadIndex,
- };
- } else if (member.kind === 'Event') {
- return {
- kind: member.kind as 'Event',
- name: member.displayName,
- };
- } else {
- return {
- kind: member.kind as 'Property' | 'PropertySignature',
- name: member.displayName,
- };
- }
- });
-}
-
-export function parametersString(item: ApiDocumentedItem & ApiParameterListMixin) {
- return resolveParameters(item).reduce((prev, cur, index) => {
- if (index === 0) {
- return `${prev}${cur.isRest ? '...' : ''}${cur.isOptional ? `${cur.name}?` : cur.name}`;
- }
-
- return `${prev}, ${cur.isRest ? '...' : ''}${cur.isOptional ? `${cur.name}?` : cur.name}`;
- }, '');
-}
diff --git a/apps/website/src/components/model/Class.tsx b/apps/website/src/components/model/Class.tsx
deleted file mode 100644
index af293ccd376f..000000000000
--- a/apps/website/src/components/model/Class.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { ApiClass, ApiConstructor } from '@discordjs/api-extractor-model';
-import { ApiItemKind } from '@discordjs/api-extractor-model';
-import { Badges } from '../Badges';
-import { Documentation } from '../documentation/Documentation';
-import { HierarchyText } from '../documentation/HierarchyText';
-import { Members } from '../documentation/Members';
-import { ObjectHeader } from '../documentation/ObjectHeader';
-import { ConstructorSection } from '../documentation/section/ConstructorSection';
-import { TypeParameterSection } from '../documentation/section/TypeParametersSection';
-import { serializeMembers } from '../documentation/util';
-import { OutlineSetter } from './OutlineSetter';
-
-export function Class({ clazz }: { readonly clazz: ApiClass }) {
- const constructor = clazz.members.find((member) => member.kind === ApiItemKind.Constructor) as
- | ApiConstructor
- | undefined;
-
- const outlineMembers = serializeMembers(clazz);
-
- return (
-
-
-
-
-
- {clazz.typeParameters.length ? : null}
- {constructor ? : null}
-
-
-
- );
-}
diff --git a/apps/website/src/components/model/Event.tsx b/apps/website/src/components/model/Event.tsx
deleted file mode 100644
index 8fed2da56cb2..000000000000
--- a/apps/website/src/components/model/Event.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import type { ApiDeclaredItem, ApiItemContainerMixin, ApiEvent } from '@discordjs/api-extractor-model';
-import { Badges } from '../Badges';
-import { CodeHeading } from '../CodeHeading';
-import { InheritanceText } from '../InheritanceText';
-import { ParameterTable } from '../ParameterTable';
-import { TSDoc } from '../documentation/tsdoc/TSDoc';
-
-export function Event({
- item,
- inheritedFrom,
-}: {
- readonly inheritedFrom?: (ApiDeclaredItem & ApiItemContainerMixin) | undefined;
- readonly item: ApiEvent;
-}) {
- const hasSummary = Boolean(item.tsdocComment?.summarySection);
-
- return (
-
-
-
-
- {item.name}
-
-
- {hasSummary || inheritedFrom ? (
-
- {item.tsdocComment ?
: null}
- {item.parameters.length ?
: null}
- {inheritedFrom ?
: null}
-
- ) : null}
-
- );
-}
diff --git a/apps/website/src/components/model/Interface.tsx b/apps/website/src/components/model/Interface.tsx
deleted file mode 100644
index d981487405ac..000000000000
--- a/apps/website/src/components/model/Interface.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { ApiInterface } from '@discordjs/api-extractor-model';
-import { Documentation } from '../documentation/Documentation';
-import { HierarchyText } from '../documentation/HierarchyText';
-import { Members } from '../documentation/Members';
-import { ObjectHeader } from '../documentation/ObjectHeader';
-import { TypeParameterSection } from '../documentation/section/TypeParametersSection';
-import { serializeMembers } from '../documentation/util';
-import { OutlineSetter } from './OutlineSetter';
-
-export function Interface({ item }: { readonly item: ApiInterface }) {
- const outlineMembers = serializeMembers(item);
-
- return (
-
-
-
- {item.typeParameters.length ? : null}
-
-
-
- );
-}
diff --git a/apps/website/src/components/model/OutlineSetter.tsx b/apps/website/src/components/model/OutlineSetter.tsx
deleted file mode 100644
index d07aaa127237..000000000000
--- a/apps/website/src/components/model/OutlineSetter.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-'use client';
-
-import { useEffect, type PropsWithChildren } from 'react';
-import { useOutline } from '~/contexts/outline';
-import type { TableOfContentsSerialized } from '../TableOfContentItems';
-
-export function OutlineSetter({ members }: PropsWithChildren<{ readonly members: TableOfContentsSerialized[] }>) {
- const { setMembers } = useOutline();
-
- useEffect(() => {
- setMembers(members);
-
- return () => {
- setMembers(null);
- };
- }, [members, setMembers]);
-
- return null;
-}
diff --git a/apps/website/src/components/model/TypeAlias.tsx b/apps/website/src/components/model/TypeAlias.tsx
deleted file mode 100644
index d15f861b4a52..000000000000
--- a/apps/website/src/components/model/TypeAlias.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { ExcerptTokenKind, type ApiTypeAlias, ExcerptToken } from '@discordjs/api-extractor-model';
-import { useMemo } from 'react';
-import { SyntaxHighlighter } from '../SyntaxHighlighter';
-import { Documentation } from '../documentation/Documentation';
-import { Header } from '../documentation/Header';
-import { SummarySection } from '../documentation/section/SummarySection';
-import { UnionMembersSection } from '../documentation/section/UnionMembersSection';
-
-export function TypeAlias({ item }: { readonly item: ApiTypeAlias }) {
- const union = useMemo(() => {
- const union: ExcerptToken[][] = [];
- let currentUnionMember: ExcerptToken[] = [];
- let depth = 0;
- for (const token of item.typeExcerpt.spannedTokens) {
- if (token.text.includes('?')) {
- return [item.typeExcerpt.spannedTokens];
- }
-
- depth += token.text.split('<').length - token.text.split('>').length;
-
- if (token.text.trim() === '|' && depth === 0) {
- if (currentUnionMember.length) {
- union.push(currentUnionMember);
- currentUnionMember = [];
- }
- } else if (depth === 0 && token.kind === ExcerptTokenKind.Content && token.text.includes('|')) {
- for (const [idx, tokenpart] of token.text.split('|').entries()) {
- if (currentUnionMember.length && depth === 0 && idx === 0) {
- currentUnionMember.push(new ExcerptToken(ExcerptTokenKind.Content, tokenpart));
- union.push(currentUnionMember);
- currentUnionMember = [];
- } else if (currentUnionMember.length && depth === 0) {
- union.push(currentUnionMember);
- currentUnionMember = [new ExcerptToken(ExcerptTokenKind.Content, tokenpart)];
- } else if (tokenpart.length) {
- currentUnionMember.push(new ExcerptToken(ExcerptTokenKind.Content, tokenpart));
- }
- }
- } else {
- currentUnionMember.push(token);
- }
- }
-
- if (currentUnionMember.length) {
- union.push(currentUnionMember);
- }
-
- return union;
- }, [item]);
-
- return (
-
-
- {/* @ts-expect-error async component */}
-
-
- {union.length ? : null}
-
- );
-}
diff --git a/apps/website/src/components/model/Variable.tsx b/apps/website/src/components/model/Variable.tsx
deleted file mode 100644
index 7a260ad74b10..000000000000
--- a/apps/website/src/components/model/Variable.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { ApiVariable } from '@discordjs/api-extractor-model';
-import { Documentation } from '../documentation/Documentation';
-import { ObjectHeader } from '../documentation/ObjectHeader';
-
-export function Variable({ item }: { readonly item: ApiVariable }) {
- return (
-
-
-
- );
-}
diff --git a/apps/website/src/components/model/enum/Enum.tsx b/apps/website/src/components/model/enum/Enum.tsx
deleted file mode 100644
index 32240fa7f2da..000000000000
--- a/apps/website/src/components/model/enum/Enum.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import type { ApiEnum } from '@discordjs/api-extractor-model';
-import { VscSymbolEnum } from '@react-icons/all-files/vsc/VscSymbolEnum';
-import { Panel } from '../../Panel';
-import { Documentation } from '../../documentation/Documentation';
-import { ObjectHeader } from '../../documentation/ObjectHeader';
-import { DocumentationSection } from '../../documentation/section/DocumentationSection';
-import { EnumMember } from './EnumMember';
-
-export function Enum({ item }: { readonly item: ApiEnum }) {
- return (
-
-
- } padded title="Members">
-
- {item.members.map((member, idx) => (
-
-
-
- ))}
-
-
-
- );
-}
diff --git a/apps/website/src/components/model/enum/EnumMember.tsx b/apps/website/src/components/model/enum/EnumMember.tsx
deleted file mode 100644
index 21bb67defac7..000000000000
--- a/apps/website/src/components/model/enum/EnumMember.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import type { ApiEnumMember } from '@discordjs/api-extractor-model';
-import { CodeHeading } from '~/components/CodeHeading';
-import { SignatureText } from '../../SignatureText';
-import { TSDoc } from '../../documentation/tsdoc/TSDoc';
-
-export function EnumMember({ member }: { readonly member: ApiEnumMember }) {
- return (
-
-
- {member.name}
- =
- {member.initializerExcerpt ? (
-
- ) : null}
-
- {member.tsdocComment ? : null}
-
- );
-}
diff --git a/apps/website/src/components/model/function/Function.tsx b/apps/website/src/components/model/function/Function.tsx
deleted file mode 100644
index 7edaaef4ca61..000000000000
--- a/apps/website/src/components/model/function/Function.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import type { ApiFunction } from '@discordjs/api-extractor-model';
-import dynamic from 'next/dynamic';
-import { Documentation } from '~/components/documentation/Documentation';
-import { ObjectHeader } from '~/components/documentation/ObjectHeader';
-import { FunctionBody } from './FunctionBody';
-
-const OverloadSwitcher = dynamic(async () => import('../../OverloadSwitcher'));
-
-export function Function({ item }: { readonly item: ApiFunction }) {
- if (item.getMergedSiblings().length > 1) {
- const overloads = item.getMergedSiblings().map((sibling, idx) => (
-
-
-
-
- ));
-
- return ;
- }
-
- return (
-
-
-
-
- );
-}
diff --git a/apps/website/src/components/model/function/FunctionBody.tsx b/apps/website/src/components/model/function/FunctionBody.tsx
deleted file mode 100644
index 4b809da0301b..000000000000
--- a/apps/website/src/components/model/function/FunctionBody.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import type { ApiFunction } from '@discordjs/api-extractor-model';
-import { ParameterSection } from '../../documentation/section/ParametersSection';
-import { TypeParameterSection } from '../../documentation/section/TypeParametersSection';
-
-export interface FunctionBodyProps {
- mergedSiblingCount: number;
- overloadDocumentation: React.ReactNode[];
-}
-
-export function FunctionBody({ item }: { readonly item: ApiFunction }) {
- return (
- <>
- {item.typeParameters.length ? : null}
- {item.parameters.length ? : null}
- >
- );
-}
diff --git a/apps/website/src/components/model/method/Method.tsx b/apps/website/src/components/model/method/Method.tsx
deleted file mode 100644
index c42158403b09..000000000000
--- a/apps/website/src/components/model/method/Method.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import {
- ApiItemKind,
- type ApiDeclaredItem,
- type ApiItemContainerMixin,
- type ApiMethod,
- type ApiMethodSignature,
-} from '@discordjs/api-extractor-model';
-import dynamic from 'next/dynamic';
-import { Fragment } from 'react';
-import { MethodDocumentation } from './MethodDocumentation';
-import { MethodHeader } from './MethodHeader';
-
-const OverloadSwitcher = dynamic(async () => import('../../OverloadSwitcher'));
-
-export function Method({
- method,
- inheritedFrom,
-}: {
- readonly inheritedFrom?: (ApiDeclaredItem & ApiItemContainerMixin) | undefined;
- readonly method: ApiMethod | ApiMethodSignature;
-}) {
- if (
- method
- .getMergedSiblings()
- .filter((sibling) => sibling.kind === ApiItemKind.Method || sibling.kind === ApiItemKind.MethodSignature).length >
- 1
- ) {
- // We have overloads, use the overload switcher, but render
- // each overload node on the server.
- const overloads = method
- .getMergedSiblings()
- .filter((sibling) => sibling.kind === ApiItemKind.Method || sibling.kind === ApiItemKind.MethodSignature)
- .map((sibling, idx) => (
-
-
-
-
- ));
-
- return ;
- }
-
- // We have just a single method, render it on the server.
- return (
- <>
-
-
- >
- );
-}
diff --git a/apps/website/src/components/model/method/MethodDocumentation.tsx b/apps/website/src/components/model/method/MethodDocumentation.tsx
deleted file mode 100644
index b64f68417637..000000000000
--- a/apps/website/src/components/model/method/MethodDocumentation.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import {
- ApiItemKind,
- type ApiDeclaredItem,
- type ApiItemContainerMixin,
- type ApiMethod,
- type ApiMethodSignature,
-} from '@discordjs/api-extractor-model';
-import { ParameterSection } from '~/components/documentation/section/ParametersSection';
-import { TypeParameterSection } from '~/components/documentation/section/TypeParametersSection';
-import { InheritanceText } from '../../InheritanceText';
-import { TSDoc } from '../../documentation/tsdoc/TSDoc';
-
-export interface MethodDocumentationProps {
- readonly inheritedFrom?: (ApiDeclaredItem & ApiItemContainerMixin) | undefined;
- readonly method: ApiMethod | ApiMethodSignature;
-}
-
-export function MethodDocumentation({ method, inheritedFrom }: MethodDocumentationProps) {
- const parent = method.parent as ApiDeclaredItem;
- const firstOverload = method
- .getMergedSiblings()
- .find(
- (meth): meth is ApiMethod => meth.kind === ApiItemKind.Method && (meth as ApiMethod).overloadIndex === 1,
- )?.tsdocComment;
-
- if (!(method.tsdocComment?.summarySection || firstOverload?.summarySection || method.parameters.length > 0)) {
- return null;
- }
-
- return (
-
- {method.tsdocComment || firstOverload ? (
-
- ) : null}
- {method.typeParameters.length ?
: null}
- {method.parameters.length ?
: null}
- {inheritedFrom && parent ?
: null}
-
- );
-}
diff --git a/apps/website/src/components/model/method/MethodHeader.tsx b/apps/website/src/components/model/method/MethodHeader.tsx
deleted file mode 100644
index 973773cc0f7a..000000000000
--- a/apps/website/src/components/model/method/MethodHeader.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import type { ApiMethod, ApiMethodSignature } from '@discordjs/api-extractor-model';
-import { useMemo } from 'react';
-import { Badges } from '~/components/Badges';
-import { CodeHeading } from '~/components/CodeHeading';
-import { ExcerptText } from '~/components/ExcerptText';
-import { parametersString } from '~/components/documentation/util';
-
-export function MethodHeader({ method }: { readonly method: ApiMethod | ApiMethodSignature }) {
- const key = useMemo(
- () => `${method.displayName}${method.overloadIndex && method.overloadIndex > 1 ? `:${method.overloadIndex}` : ''}`,
- [method.displayName, method.overloadIndex],
- );
-
- return (
-
-
-
-
- {`${method.name}(${parametersString(method)})`}
- :
-
-
-
-
- );
-}
diff --git a/apps/website/src/components/ui/Alert.tsx b/apps/website/src/components/ui/Alert.tsx
new file mode 100644
index 000000000000..276d21368e85
--- /dev/null
+++ b/apps/website/src/components/ui/Alert.tsx
@@ -0,0 +1,67 @@
+import { VscFlame } from '@react-icons/all-files/vsc/VscFlame';
+import { VscInfo } from '@react-icons/all-files/vsc/VscInfo';
+import { VscWarning } from '@react-icons/all-files/vsc/VscWarning';
+import type { PropsWithChildren } from 'react';
+
+interface IAlert {
+ readonly title?: string | undefined;
+ readonly type: 'danger' | 'info' | 'success' | 'warning';
+}
+
+function resolveType(type: IAlert['type']) {
+ switch (type) {
+ case 'danger': {
+ return {
+ text: 'text-red-500',
+ border: 'border-red-500',
+ icon: ,
+ };
+ }
+
+ case 'info': {
+ return {
+ text: 'text-blue-500',
+ border: 'border-blue-500',
+ icon: ,
+ };
+ }
+
+ case 'success': {
+ return {
+ text: 'text-green-500',
+ border: 'border-green-500',
+ icon: ,
+ };
+ }
+
+ case 'warning': {
+ return {
+ text: 'text-yellow-500',
+ border: 'border-yellow-500',
+ icon: ,
+ };
+ }
+ }
+}
+
+export async function Alert({ title, type, children }: PropsWithChildren) {
+ const { text, border, icon } = resolveType(type);
+
+ return (
+
+
+
{children}
+
+
+
+
+ {icon}
+ {title ? {title} : null}
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/ui/Button.tsx b/apps/website/src/components/ui/Button.tsx
new file mode 100644
index 000000000000..f469c6a53ef2
--- /dev/null
+++ b/apps/website/src/components/ui/Button.tsx
@@ -0,0 +1,3 @@
+'use client';
+
+export { Button } from 'react-aria-components';
diff --git a/apps/website/src/components/ui/CmdK.tsx b/apps/website/src/components/ui/CmdK.tsx
new file mode 100644
index 000000000000..5b5eb48e46a0
--- /dev/null
+++ b/apps/website/src/components/ui/CmdK.tsx
@@ -0,0 +1,148 @@
+'use client';
+
+import { Command } from 'cmdk';
+import { useAtom, useSetAtom } from 'jotai';
+import { ArrowRight } from 'lucide-react';
+import MeiliSearch from 'meilisearch';
+import { usePathname, useRouter } from 'next/navigation';
+import { useEffect, useMemo, useState } from 'react';
+import { useDebounceValue } from 'usehooks-ts';
+import { isCmdKOpenAtom } from '~/stores/cmdk';
+import { isDrawerOpenAtom } from '~/stores/drawer';
+import { resolveKind } from '~/util/resolveNodeKind';
+import { OverlayScrollbarsComponent } from '../OverlayScrollbars';
+
+const client = new MeiliSearch({
+ host: 'https://search.discordjs.dev',
+ apiKey: 'b51923c6abb574b1e97be9a03dc6414b6c69fb0c5696d0ef01a82b0f77d223db',
+});
+
+export function CmdK({ dependencies }: { readonly dependencies: string[] }) {
+ const pathname = usePathname();
+ const router = useRouter();
+ const [open, setOpen] = useAtom(isCmdKOpenAtom);
+ const setDrawerOpen = useSetAtom(isDrawerOpenAtom);
+ const [search, setSearch] = useDebounceValue('', 250);
+ const [searchResults, setSearchResults] = useState([]);
+
+ const packageName = useMemo(() => pathname?.split('/').slice(3, 4)[0], [pathname]);
+ const branchName = useMemo(() => pathname?.split('/').slice(4, 5)[0], [pathname]);
+
+ const searchResultItems = useMemo(
+ () =>
+ searchResults?.map((item, idx) => (
+ {
+ router.push(item.path);
+ setOpen(false);
+ }}
+ value={item.id}
+ >
+ {resolveKind(item.kind)}
+
+ {item.name}
+ {item.summary}
+ {item.path}
+
+
+
+ )) ?? [],
+ [router, searchResults, setOpen],
+ );
+
+ // Toggle the menu when ⌘K is pressed
+ useEffect(() => {
+ const down = (event: KeyboardEvent) => {
+ if (event.key === 'k' && (event.metaKey || event.ctrlKey)) {
+ event.preventDefault();
+ setOpen((open) => !open);
+ }
+ };
+
+ document.addEventListener('keydown', down);
+ return () => {
+ document.removeEventListener('keydown', down);
+ };
+ }, [setOpen]);
+
+ useEffect(() => {
+ if (open) {
+ setDrawerOpen(false);
+ setSearch('');
+ }
+
+ return () => {
+ document.body.style.pointerEvents = 'auto';
+ };
+ }, [open, setDrawerOpen, setSearch]);
+
+ useEffect(() => {
+ // const searchDoc = async (searchString: string, version: string) => {
+ // console.log(dependencies);
+ // const res = await client
+ // .index(`${packageName?.replaceAll('.', '-')}-${version}`)
+ // .search(searchString, { limit: 25 });
+ // setSearchResults(res.hits);
+ // };
+
+ const searchDoc = async (searchString: string, version: string) => {
+ const result = await client.multiSearch({
+ queries: [`${packageName?.replaceAll('.', '-')}-${version}`, ...dependencies].map((dep) => ({
+ indexUid: dep,
+ // eslint-disable-next-line id-length
+ q: searchString,
+ limit: 25,
+ attributesToSearchOn: ['name'],
+ })),
+ });
+ setSearchResults(result.results.flatMap((res) => res.hits));
+ };
+
+ if (search && packageName) {
+ void searchDoc(search, branchName?.replaceAll('.', '-') ?? 'main');
+ } else {
+ setSearchResults([]);
+ }
+ }, [branchName, dependencies, packageName, search]);
+
+ return (
+
+
+
+
+ {search && searchResultItems.length ? (
+ searchResultItems
+ ) : (
+
+ No results found.
+
+ )}
+
+
+
+ );
+}
diff --git a/apps/website/src/components/ui/Collapsible.tsx b/apps/website/src/components/ui/Collapsible.tsx
new file mode 100644
index 000000000000..5d19873e940c
--- /dev/null
+++ b/apps/website/src/components/ui/Collapsible.tsx
@@ -0,0 +1,3 @@
+'use client';
+
+export { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@radix-ui/react-collapsible';
diff --git a/apps/website/src/components/ui/Drawer.tsx b/apps/website/src/components/ui/Drawer.tsx
new file mode 100644
index 000000000000..48979e391458
--- /dev/null
+++ b/apps/website/src/components/ui/Drawer.tsx
@@ -0,0 +1,37 @@
+'use client';
+
+import { useAtom } from 'jotai';
+import { ChevronUp } from 'lucide-react';
+import { useEffect, type PropsWithChildren } from 'react';
+import { useMediaQuery } from 'usehooks-ts';
+import { Drawer as Vaul } from 'vaul';
+import { isDrawerOpenAtom } from '~/stores/drawer';
+
+export function Drawer({ children }: PropsWithChildren) {
+ const [open, setOpen] = useAtom(isDrawerOpenAtom);
+ const isMedium = useMediaQuery('(min-width: 768px)');
+
+ useEffect(() => {
+ if (isMedium) {
+ setOpen(false);
+ }
+ }, [isMedium, setOpen]);
+
+ return (
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/apps/website/src/components/Footer.tsx b/apps/website/src/components/ui/Footer.tsx
similarity index 70%
rename from apps/website/src/components/Footer.tsx
rename to apps/website/src/components/ui/Footer.tsx
index b07ec0494fef..26a79cf0efea 100644
--- a/apps/website/src/components/Footer.tsx
+++ b/apps/website/src/components/ui/Footer.tsx
@@ -2,13 +2,13 @@ import Image from 'next/image';
import vercelLogo from '~/assets/powered-by-vercel.svg';
import workersLogo from '~/assets/powered-by-workers.png';
-export default function Footer() {
+export function Footer() {
return (