├── .eslintrc.json ├── .gitattributes ├── .github └── workflows │ ├── changelog.yaml │ └── test.yaml ├── .gitignore ├── .prettierrc.json ├── README.md ├── ci ├── changelog.sh ├── checkLinks.ts └── tsconfig.json ├── components ├── Alert.tsx ├── Badge.tsx ├── Copy.tsx ├── Footer.tsx ├── Header.tsx ├── MDX.tsx ├── Menu.tsx ├── Navigation.tsx ├── OpenGraph.tsx ├── RouteHeader.tsx ├── Snowflake.tsx ├── ThemeSwitcher.tsx ├── YouTubeEmbed.tsx ├── icons │ ├── Bars.tsx │ ├── Caret.tsx │ ├── CaretFill.tsx │ ├── Check.tsx │ ├── Chevron.tsx │ ├── Copy.tsx │ ├── Discord.tsx │ ├── File.tsx │ ├── Gear.tsx │ ├── Hyperlink.tsx │ ├── Lightbulb.tsx │ ├── Moon.tsx │ └── Sun.tsx └── mdx │ ├── Anchor.tsx │ ├── Code.tsx │ ├── ContentWrapper.tsx │ ├── Emphasis.tsx │ ├── Heading.tsx │ ├── HorizontalRule.tsx │ ├── InlineCode.tsx │ ├── List.tsx │ ├── Paragraph.tsx │ ├── StrikeThrough.tsx │ ├── Strong.tsx │ └── Table.tsx ├── contexts └── MenuContext.tsx ├── hooks ├── useClipboard.tsx ├── useOnClickOutside.tsx └── useToggle.tsx ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── 404.tsx ├── _app.tsx ├── changelog.mdx ├── dispatch │ ├── branches-and-builds.mdx │ ├── dispatch-and-you.mdx │ ├── error-codes.mdx │ ├── field-values.mdx │ └── list-of-commands.mdx ├── game-and-server-management │ ├── alpha-and-beta-testing.mdx │ ├── how-to-get-your-game-on-discord.mdx │ └── special-channels.mdx ├── game-sdk │ ├── achievements.mdx │ ├── activities.mdx │ ├── applications.mdx │ ├── discord-voice.mdx │ ├── discord.mdx │ ├── images.mdx │ ├── lobbies.mdx │ ├── networking.mdx │ ├── overlay.mdx │ ├── relationships.mdx │ ├── sdk-starter-guide.mdx │ ├── storage.mdx │ ├── store.mdx │ └── users.mdx ├── index.tsx ├── interactions │ ├── application-commands.mdx │ ├── message-components.mdx │ ├── receiving-and-responding.mdx │ └── slash-commands.mdx ├── intro.mdx ├── legal.mdx ├── menu.tsx ├── policy.mdx ├── reference.mdx ├── resources │ ├── application.mdx │ ├── audit-log.mdx │ ├── channel.mdx │ ├── emoji.mdx │ ├── guild-template.mdx │ ├── guild.mdx │ ├── invite.mdx │ ├── message.mdx │ ├── stage-instance.mdx │ ├── sticker.mdx │ ├── user.mdx │ ├── voice.mdx │ └── webhook.mdx ├── rich-presence │ ├── best-practices.mdx │ ├── faq.mdx │ ├── how-to.mdx │ └── launch-checklist.mdx ├── store-distribution-agreement.mdx ├── topics │ ├── certified-devices.mdx │ ├── community-resources.mdx │ ├── gateway.mdx │ ├── oauth2.mdx │ ├── opcodes-and-status-codes.mdx │ ├── permissions.mdx │ ├── rate-limits.mdx │ ├── rpc.mdx │ ├── teams.mdx │ ├── threads.mdx │ └── voice-connections.mdx └── typography.mdx ├── postcss.config.js ├── public └── images │ ├── available-published.png │ └── gift-code-creation.png ├── stylesheets ├── prism.css ├── scrollbar.css ├── snowflake-deconstruction.css ├── styles.css ├── tailwind.css ├── whitney │ ├── WhitneySemiboldItalic.woff │ ├── whitney.css │ ├── whitneybold.woff │ ├── whitneybook.woff │ ├── whitneybookitalic.woff │ ├── whitneylight.woff │ ├── whitneylightitalic.woff │ ├── whitneymedium.woff │ ├── whitneymediumitalic.woff │ └── whitneysemibold.woff └── youtube.css ├── tailwind.config.js ├── tsconfig.eslint.json └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "marine/prettier/react"], 3 | "ignorePatterns": ["dist/*", ".next/*", "**/*.js", "next-env.d.ts"], 4 | "parserOptions": { 5 | "project": "./tsconfig.eslint.json" 6 | }, 7 | "overrides": [ 8 | { 9 | "files": ["ci/*"], 10 | "extends": "marine/prettier/node" 11 | } 12 | ], 13 | "rules": { 14 | "react/react-in-jsx-scope": 0, 15 | "react/jsx-filename-extension": [ 16 | 1, 17 | { 18 | "extensions": [ 19 | ".tsx" 20 | ] 21 | } 22 | ], 23 | "no-eq-null": "off" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/workflows/changelog.yaml: -------------------------------------------------------------------------------- 1 | # TODO: On PRs, check for a modified changelog.mdx file or "bypass changelog" label. If neither exist, fail. If one exists, pass 2 | name: Changelog 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | jobs: 11 | changelog: 12 | name: Enforce Changelog 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v2 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Check Changelog 21 | run: bash ci/changelog.sh 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | GITHUB_PULL_TITLE: ${{ github.event.pull_request.title }} 25 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Testing 2 | on: [push, pull_request] 3 | jobs: 4 | lint: 5 | name: ESLint 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout repository 9 | uses: actions/checkout@v2 10 | 11 | - name: Install Node v16 12 | uses: actions/setup-node@v2 13 | with: 14 | node-version: 16 15 | cache: npm 16 | 17 | - name: Install dependencies 18 | run: npm ci 19 | 20 | - name: Run ESLint 21 | run: npm run lint 22 | 23 | prettier: 24 | name: Prettier 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v2 29 | 30 | - name: Install Node v16 31 | uses: actions/setup-node@v2 32 | with: 33 | node-version: 16 34 | cache: npm 35 | 36 | - name: Install dependencies 37 | run: npm ci 38 | 39 | - name: Run Prettier 40 | run: npm run prettier 41 | 42 | links: 43 | name: Check Links 44 | runs-on: ubuntu-latest 45 | steps: 46 | - name: Checkout repository 47 | uses: actions/checkout@v2 48 | 49 | - name: Install Node v16 50 | uses: actions/setup-node@v2 51 | with: 52 | node-version: 16 53 | cache: npm 54 | 55 | - name: Install dependencies 56 | run: npm ci 57 | 58 | - name: Build 59 | run: npm run build:ci 60 | 61 | - name: Run Link Checks 62 | run: npm run test:links 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # ts builds 16 | /dist 17 | 18 | # production 19 | /build 20 | 21 | # misc 22 | .DS_Store 23 | *.pem 24 | 25 | # debug 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # local env files 31 | .env.local 32 | .env.development.local 33 | .env.test.local 34 | .env.production.local 35 | 36 | # vercel 37 | .vercel 38 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "quoteProps": "consistent", 3 | "endOfLine": "lf", 4 | "printWidth": 120, 5 | "useTabs": true, 6 | "overrides": [{ 7 | "files": "*.mdx", 8 | "options": { 9 | "useTabs": false 10 | } 11 | }] 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # discord.dev 2 | 3 | This is a Discord hackweek project. This codebase is owned by Discord, despite being under a personal account. As is the nature of hackweek, not all projects ship - this website is an idea/suggestion, not a planned thing. 4 | 5 | If you're interested in seeing more (we're streaming through hackweek!), join us [on Discord](https://discord.gg/ian) 6 | -------------------------------------------------------------------------------- /ci/changelog.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ -z $GITHUB_PULL_TITLE ]]; then 6 | COMMIT_MSG=$(git log -1 --pretty=%B) 7 | else 8 | COMMIT_MSG=$GITHUB_PULL_TITLE 9 | fi 10 | 11 | for word in $COMMIT_MSG 12 | do 13 | if [[ "$word" == "[no-changelog]" ]]; then 14 | echo "Skipping Changelog Check" 15 | exit 0 16 | fi 17 | done 18 | 19 | git remote add temp "https://${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}" 20 | 21 | echo "Getting HEAD info..." 22 | 23 | if [[ -z $GITHUB_BASE_REF ]]; then 24 | PREVIOUS_SHA=$(git rev-parse HEAD^1 2>&1) && exit_status=$? || exit_status=$? 25 | 26 | if [[ $exit_status -ne 0 ]]; then 27 | echo "::warning::Unable to determine the previous commit sha" 28 | exit 1 29 | fi 30 | else 31 | TARGET_BRANCH=$GITHUB_BASE_REF 32 | CURRENT_BRANCH=$GITHUB_HEAD_REF 33 | git fetch temp "${TARGET_BRANCH}":"${TARGET_BRANCH}" 34 | PREVIOUS_SHA=$(git rev-parse "${TARGET_BRANCH}" 2>&1) && exit_status=$? || exit_status=$? 35 | 36 | if [[ $exit_status -ne 0 ]]; then 37 | echo "::warning::Unable to determine the base ref sha for ${TARGET_BRANCH}" 38 | exit 1 39 | fi 40 | fi 41 | 42 | git diff --diff-filter=M --name-only "$PREVIOUS_SHA" "$GITHUB_SHA" | grep "pages/changelog.mdx" && MODIFIED=true || MODIFIED=false 43 | 44 | git remote remove temp 45 | 46 | if [[ $MODIFIED == false ]]; then 47 | echo "::error file=pages/changelog.mdx,title=Missing Changelog::No Changes Found in changelog, add a changelog message or add [no-changelog] to your commit message or PR title" 48 | exit 1 49 | fi 50 | 51 | echo "Changelog modified!" 52 | -------------------------------------------------------------------------------- /ci/checkLinks.ts: -------------------------------------------------------------------------------- 1 | import { readdirSync, statSync, readFileSync } from "fs"; 2 | import path from "path"; 3 | import { JSDOM } from "jsdom"; 4 | import chalk from "chalk"; 5 | import * as github from "@actions/core"; 6 | const cwd = process.env.GITHUB_ACTIONS ? process.env.GITHUB_WORKSPACE! : process.cwd(); 7 | 8 | function importDirectory(directory: string, extension: string, subdirectories = true) { 9 | try { 10 | const output = new Map(); 11 | const files = readdirSync(directory); 12 | const requestedFiles = files.filter((name) => name.endsWith(extension)); 13 | for (const file of requestedFiles) { 14 | const currentPath = path.join(directory, file); 15 | try { 16 | const read = readFileSync(currentPath, "utf8"); 17 | output.set(`/${file}`, read); 18 | } catch { 19 | // Discard error, file is not a file, but a directory 20 | } 21 | } 22 | if (subdirectories) { 23 | for (const possibleDir of files) { 24 | const dirPath = `/${possibleDir}`; 25 | const currentPath = path.join(directory, dirPath); 26 | if (statSync(currentPath).isDirectory()) { 27 | const subdir = importDirectory(currentPath, extension, subdirectories); 28 | if (!subdir) continue; 29 | for (const [name, read] of subdir) { 30 | output.set(`${dirPath}${name}`, read); 31 | } 32 | } 33 | } 34 | } 35 | return output; 36 | } catch { 37 | // Directory likely does not exist, we should be able to safely discard this error 38 | return null; 39 | } 40 | } 41 | 42 | function scanFile( 43 | regex: RegExp, 44 | index: number, 45 | name: string, 46 | splitFile: string[], 47 | valid: Map, 48 | results: github.AnnotationProperties[] 49 | ): void { 50 | let multilineCode = false; 51 | splitFile.forEach((line, lineNum) => { 52 | if (line.startsWith("```")) { 53 | multilineCode = !multilineCode; 54 | if (line.length > 3 && line.endsWith("```")) multilineCode = !multilineCode; 55 | } 56 | if (multilineCode) return; 57 | const matches = line.matchAll(regex); 58 | 59 | for (const match of matches) { 60 | const split = match[index].split("#"); 61 | let url = split[0].endsWith("/") ? split[0].slice(0, -1) : split[0]; 62 | if (match[index].startsWith("#")) url = name; 63 | if (!valid.has(url)) { 64 | results.push({ 65 | title: `Base url ${chalk.blueBright(url)} does not exist`, 66 | startLine: lineNum + 1, 67 | startColumn: match.index, 68 | endColumn: (match.index ?? 0) + match[0].length, 69 | }); 70 | continue; 71 | } 72 | 73 | if (!split[1]) continue; 74 | const validAnchors = valid.get(url)!; 75 | if (!validAnchors.includes(split[1])) { 76 | results.push({ 77 | title: `Anchor ${chalk.cyan(split[1])} does not exist on ${chalk.blueBright(url)}`, 78 | startLine: lineNum + 1, 79 | startColumn: match.index, 80 | endColumn: (match.index ?? 0) + match[0].length, 81 | }); 82 | } 83 | } 84 | }); 85 | } 86 | 87 | const htmlFiles = importDirectory(path.join(cwd, ".next/server/pages"), ".html"); 88 | 89 | if (!htmlFiles) { 90 | console.error("No links found, ensure that build has been run!"); 91 | process.exit(1); 92 | } 93 | 94 | const validLinks = new Map(); 95 | 96 | let extLength = ".html".length; 97 | 98 | for (const [name, raw] of htmlFiles) { 99 | const keyName = name.slice(0, -extLength); 100 | if (!validLinks.has(keyName)) { 101 | validLinks.set(keyName, []); 102 | } 103 | const validAnchors = validLinks.get(keyName)!; 104 | const fragment = JSDOM.fragment(raw); 105 | // @ts-ignore 106 | const main = fragment.querySelector("main"); 107 | if (!main) continue; 108 | const allIds = main.querySelectorAll("*[id]"); 109 | for (const node of allIds.values()) { 110 | validAnchors.push(node.id); 111 | } 112 | } 113 | 114 | const results = new Map(); 115 | 116 | try { 117 | const navFile = "components/Navigation.tsx"; 118 | const nav = readFileSync(path.join(cwd, navFile), "utf8"); 119 | const file = nav.split("\n"); 120 | if (!results.has(navFile)) { 121 | results.set(navFile, []); 122 | } 123 | const ownResults = results.get(navFile)!; 124 | scanFile( 125 | /(?): void { 163 | let output = "\n"; 164 | let total = 0; 165 | for (const [resultFile, resultArr] of resultMap) { 166 | if (resultArr.length <= 0) continue; 167 | const filePath = path.join(cwd, resultFile); 168 | output += `${chalk.underline(filePath)}\n`; 169 | output += resultArr.reduce((result, props) => { 170 | total += 1; 171 | return `${result} ${props.startLine ?? ""}:${props.startColumn ?? ""}-${props.endColumn ?? ""} ${chalk.yellow( 172 | "warning" 173 | )} ${props.title ?? ""}\n`; 174 | }, ""); 175 | output += "\n"; 176 | } 177 | output += "\n"; 178 | if (total > 0) { 179 | output += chalk.red.bold(`\u2716 ${total} problem${total === 1 ? "" : "s"}\n`); 180 | } 181 | console.log(output); 182 | } 183 | 184 | function annotateResults(resultMap: Map): void { 185 | let total = 0; 186 | for (const [resultFile, resultArr] of resultMap) { 187 | if (resultArr.length <= 0) continue; 188 | github.startGroup(resultFile); 189 | for (const result of resultArr) { 190 | total += 1; 191 | console.log( 192 | `::warning file=${resultFile},title=Invalid Link,line=${result.startLine ?? 0},endLine=${ 193 | result.startLine ?? 0 194 | },col=${result.startColumn ?? 0},endColumn=${result.endColumn ?? result.startColumn ?? 0}::${ 195 | result.title ?? "Invalid Link" 196 | }` 197 | ); 198 | } 199 | github.endGroup(); 200 | } 201 | if (total > 0) { 202 | github.setFailed("One or more links are invalid!"); 203 | } 204 | } 205 | 206 | if (results.size > 0) { 207 | if (process.env.GITHUB_ACTIONS) { 208 | annotateResults(results); 209 | } else { 210 | printResults(results); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /ci/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2021", 4 | "lib": ["ESNext"], 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "alwaysStrict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "esModuleInterop": true, 10 | "module": "commonjs", 11 | "moduleResolution": "node", 12 | "importsNotUsedAsValues": "error", 13 | "isolatedModules": true, 14 | "rootDir": "./", 15 | "outDir": "../dist/ci" 16 | }, 17 | "include": ["**/*.ts"], 18 | "exclude": ["node_modules"] 19 | } -------------------------------------------------------------------------------- /components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | 3 | type AlertType = "danger" | "warn" | "info"; 4 | 5 | function getClasses(type: AlertType) { 6 | return classNames("block my-4 px-4 dark:bg-opacity-50 border-2 dark:border-opacity-50 rounded-lg overflow-auto", { 7 | "bg-red-100 border-red-500 dark:bg-red-900": type === "danger", 8 | "bg-yellow-100 border-yellow-600 dark:bg-yellow-900": type === "warn", 9 | "bg-blue-100 border-blue-500 dark:bg-blue-900": type === "info", 10 | }); 11 | } 12 | 13 | interface AlertProps { 14 | type: AlertType; 15 | children: React.ReactNode; 16 | } 17 | 18 | export default function Alert({ type, children }: AlertProps) { 19 | return ; 20 | } 21 | -------------------------------------------------------------------------------- /components/Badge.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | interface BadgeProps { 4 | href: string; 5 | tooltip: string; 6 | name: string; 7 | } 8 | 9 | export default function Badge({ href, name, tooltip }: BadgeProps) { 10 | return ( 11 | 12 | 13 | 14 | {name} 15 | 16 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /components/Copy.tsx: -------------------------------------------------------------------------------- 1 | import classnames from "classnames"; 2 | import { CopyStatus, useClipboard } from "../hooks/useClipboard"; 3 | 4 | interface CopyButtonProps { 5 | text: string; 6 | children: React.ReactNode; 7 | } 8 | 9 | export default function CopyButton({ text, children }: CopyButtonProps) { 10 | const { copy, status } = useClipboard(text); 11 | let value = children; 12 | 13 | if (status === CopyStatus.SUCCESS) { 14 | value = "Copied!"; 15 | } else if (status === CopyStatus.ERROR) { 16 | value = "Copy failed :("; 17 | } 18 | 19 | const classes = classnames("clipboard", { 20 | clipboard_notification: status !== CopyStatus.INACTIVE, 21 | }); 22 | 23 | return ( 24 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /components/Footer.tsx: -------------------------------------------------------------------------------- 1 | export default function Footer() { 2 | return null; 3 | // return
Feet
; 4 | } 5 | -------------------------------------------------------------------------------- /components/Header.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useContext } from "react"; 2 | import Link from "next/link"; 3 | import Bars from "./icons/Bars"; 4 | import MenuContext from "../contexts/MenuContext"; 5 | import ThemeSwitcher from "./ThemeSwitcher"; 6 | import Discord from "./icons/Discord"; 7 | 8 | export default function Header() { 9 | const { setOpen } = useContext(MenuContext); 10 | 11 | const onMenuClick = useCallback( 12 | (event) => { 13 | event.preventDefault(); 14 | setOpen(); 15 | }, 16 | [setOpen] 17 | ); 18 | 19 | return ( 20 |
21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /components/MDX.tsx: -------------------------------------------------------------------------------- 1 | import { MDXProvider } from "@mdx-js/react"; 2 | import ContentWrapper from "./mdx/ContentWrapper"; 3 | import { H1, H2, H3, H4, H5, H6 } from "./mdx/Heading"; 4 | import Code from "./mdx/Code"; 5 | import Paragraph from "./mdx/Paragraph"; 6 | import { ListItem, OrderedList, UnorderedList } from "./mdx/List"; 7 | import { Table, TableHead, TableData, TableHeader, TableRow } from "./mdx/Table"; 8 | import Strong from "./mdx/Strong"; 9 | import Emphasis from "./mdx/Emphasis"; 10 | import StrikeThrough from "./mdx/StrikeThrough"; 11 | import HorizontalRule from "./mdx/HorizontalRule"; 12 | import Anchor from "./mdx/Anchor"; 13 | import InlineCode from "./mdx/InlineCode"; 14 | 15 | import RouteHeader from "./RouteHeader"; 16 | import Alert from "./Alert"; 17 | import Image from "next/image"; 18 | 19 | const COMPONENTS = { 20 | wrapper: (props: any) => , 21 | h1: H1, 22 | h2: H2, 23 | h3: H3, 24 | h4: H4, 25 | h5: H5, 26 | h6: H6, 27 | p: Paragraph, 28 | code: Code, 29 | inlineCode: InlineCode, 30 | ul: UnorderedList, 31 | ol: OrderedList, 32 | li: ListItem, 33 | table: Table, 34 | thead: TableHead, 35 | th: TableHeader, 36 | tr: TableRow, 37 | td: TableData, 38 | em: Emphasis, 39 | strong: Strong, 40 | delete: StrikeThrough, 41 | hr: HorizontalRule, 42 | a: Anchor, 43 | 44 | // Custom components 45 | Image, 46 | Alert, 47 | RouteHeader, 48 | }; 49 | 50 | interface MDXProps { 51 | children: React.ReactNode; 52 | } 53 | 54 | export default function MDX({ children }: MDXProps) { 55 | return {children}; 56 | } 57 | -------------------------------------------------------------------------------- /components/Menu.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useRef } from "react"; 2 | import classNames from "classnames"; 3 | import Bars from "./icons/Bars"; 4 | import Navigation from "./Navigation"; 5 | import MenuContext from "../contexts/MenuContext"; 6 | import useOnClickOutside from "../hooks/useOnClickOutside"; 7 | import { useRouter } from "next/router"; 8 | 9 | export default function Menu() { 10 | const ref = useRef(null); 11 | const router = useRouter(); 12 | const { open, setClose } = useContext(MenuContext); 13 | 14 | const classes = classNames( 15 | [ 16 | "text-theme-light-text absolute -left-full pr-16 md:pr-0 top-0 w-full h-full flex z-40 transition-transform duration-300 transform-gpu", 17 | "md:flex md:flex-shrink-0 md:left-auto md:relative md:w-auto md:transform-none md:transition-none", 18 | ], 19 | { 20 | "translate-x-full ": open, 21 | "translate-x-none md:flex": !open, 22 | } 23 | ); 24 | 25 | useEffect(() => { 26 | const handler = () => { 27 | if (open) { 28 | setClose(); 29 | } 30 | }; 31 | 32 | router.events.on("routeChangeComplete", handler); 33 | return () => router.events.on("routeChangeComplete", handler); 34 | }, [router.events, open, setClose]); 35 | 36 | useOnClickOutside(ref, setClose); 37 | 38 | return ( 39 |
40 |
41 |
42 |
43 | 44 | 45 |
46 |
47 |
48 |
49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /components/OpenGraph.tsx: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import { useRouter } from "next/router"; 3 | 4 | interface OpenGraphProps { 5 | title?: string; 6 | description?: string; 7 | } 8 | 9 | export default function OpenGraph({ 10 | title = "Discord Developers", 11 | description = "👾 BOTS BOTS BOTS 👾", 12 | }: OpenGraphProps) { 13 | const router = useRouter(); 14 | const url = `https://discord.com/developers/docs${router.asPath}`; 15 | 16 | return ( 17 | 18 | 19 | 20 | {/* Twitter */} 21 | 22 | 23 | 24 | {/* Open Graph */} 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /components/RouteHeader.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from "react"; 2 | import classNames from "classnames"; 3 | import { H3 } from "./mdx/Heading"; 4 | import Badge from "./Badge"; 5 | 6 | type RESTMethod = "GET" | "POST" | "PATCH" | "PUT" | "DELETE"; 7 | 8 | interface MethodBadgeProps { 9 | method: RESTMethod; 10 | } 11 | 12 | function MethodBadge({ method }: MethodBadgeProps) { 13 | const name = method.toUpperCase(); 14 | 15 | const classes = classNames("px-2 py-1 text-sm dark:bg-opacity-50 border-2 dark:border-opacity-50 rounded uppercase", { 16 | "bg-blue-100 text-blue-700 dark:bg-blue-600 dark:text-white border-blue-500": name === "GET", 17 | "bg-green-100 text-green-700 dark:bg-green-600 dark:text-white border-green-500": name === "POST", 18 | "bg-yellow-100 text-yellow-700 dark:bg-yellow-700 dark:text-white border-yellow-500": 19 | name === "PATCH" || name === "PUT", 20 | "bg-red-100 text-red-500 dark:bg-red-700 dark:text-white border-red-500": name === "DELETE", 21 | }); 22 | 23 | return {method}; 24 | } 25 | 26 | interface RouteHeaderProps { 27 | method: RESTMethod; 28 | url: string; 29 | children: React.ReactNode; 30 | supportsXAuditLogHeader?: boolean; 31 | requestDoesNotRequireAuthorizationHeader?: boolean; 32 | } 33 | 34 | export default function RouteHeader({ 35 | method, 36 | url, 37 | children, 38 | supportsXAuditLogHeader, 39 | requestDoesNotRequireAuthorizationHeader, 40 | }: RouteHeaderProps) { 41 | return ( 42 | 43 |

{children}

44 |
45 | 46 | {url} 47 |
48 |
49 | {requestDoesNotRequireAuthorizationHeader ? ( 50 | 55 | ) : null} 56 | {supportsXAuditLogHeader ? ( 57 | 62 | ) : null} 63 |
64 |
65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /components/Snowflake.tsx: -------------------------------------------------------------------------------- 1 | export default function Snowflake(props: unknown) { 2 | return ( 3 | 11 | 15 | 175928847299117063 16 | 17 | 18 | to binary 19 | 20 | 21 | Number of milliseconds since the Discord epoch (first second of 2015) 22 | 23 | 27 | internal 28 | 29 | worker 30 | 31 | 32 | ID 33 | 34 | 35 | 39 | internal 40 | 41 | process 42 | 43 | 44 | ID 45 | 46 | 47 | 51 | incremented for every 52 | 53 | generated ID on that 54 | 55 | 56 | process 57 | 58 | 59 | 63 | 000000100111000100000110010110101100000100 64 | 65 | 00001 66 | 67 | 68 | 00000 69 | 70 | 71 | 000000000111 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 41944705796 92 | 93 | 94 | 1462015105796 95 | 96 | 97 | 0 98 | 99 | 100 | 12 101 | 102 | 103 | 17 104 | 105 | 106 | 22 107 | 108 | 109 | 64 110 | 111 | 112 | + 1420070400000 113 | 114 | Discord Epoch (unix timestamp in ms) 115 | 116 | 117 | 118 | Parse unix timstamp (ms) 119 | 120 | 121 | 2016-04-30 11:18:25.796 UTC 122 | 123 | 124 | to decimal 125 | 126 | 127 | ); 128 | } 129 | -------------------------------------------------------------------------------- /components/ThemeSwitcher.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment, useCallback } from "react"; 2 | import { Transition, Menu } from "@headlessui/react"; 3 | import classNames from "classnames"; 4 | import { useTheme } from "next-themes"; 5 | import Moon from "./icons/Moon"; 6 | import Sun from "./icons/Sun"; 7 | import Gear from "./icons/Gear"; 8 | import Lightbulb from "./icons/Lightbulb"; 9 | import Check from "./icons/Check"; 10 | 11 | export default function ThemeSwitcher() { 12 | const { theme, setTheme } = useTheme(); 13 | 14 | const getMenuItemClasses = useCallback( 15 | (active: boolean) => 16 | classNames("group flex items-center px-2 py-2 w-full text-sm rounded-md", { 17 | "bg-brand-blurple text-white": active, 18 | "text-gray-900 dark:text-theme-dark-sidebar-text": !active, 19 | }), 20 | [] 21 | ); 22 | 23 | return ( 24 | 25 |
26 | 27 | Change Theme 28 | 30 |
31 | 40 | 41 |
42 | 43 | {({ active }) => ( 44 | 52 | )} 53 | 54 | 55 | {({ active }) => ( 56 | 64 | )} 65 | 66 | 67 | {({ active }) => ( 68 | 76 | )} 77 | 78 |
79 |
80 |
81 |
82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /components/YouTubeEmbed.tsx: -------------------------------------------------------------------------------- 1 | export default function YouTubeEmbed({ src }: { src: string }) { 2 | return ( 3 |
4 |