Compare commits

...

105 Commits

Author SHA1 Message Date
iCrawl
09a9fb57e0 chore(core): release @discordjs/core@0.5.2 2023-04-16 23:02:41 +02:00
iCrawl
025b7aea29 chore(core): release @discordjs/core@0.5.1 2023-04-16 22:58:25 +02:00
iCrawl
7aa5ea731f chore(ws): release @discordjs/ws@0.8.1 2023-04-16 22:54:15 +02:00
iCrawl
f883279ab3 ci: re-add pr triage for labels 2023-04-16 22:50:43 +02:00
iCrawl
f1b2dec8e1 chore: add codeowners 2023-04-16 22:38:38 +02:00
Aura Román
0a1701b046 types(ChannelsAPI): use correct type for editMessage (#9399)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-16 18:27:16 +00:00
Jiralite
5dcc6df224 fix(updating-to-v14): Prioritise JavaScript (#9401) 2023-04-16 20:22:41 +02:00
Jiralite
1c35425120 fix(collection): filter() markdown (#9402) 2023-04-16 20:21:47 +02:00
Jiralite
cf1f5c0f0c chore(async-await): Update to ESM (#9403) 2023-04-16 20:21:30 +02:00
iCrawl
a210fc64a2 fix(website): dark mode text color 2023-04-16 19:21:41 +02:00
iCrawl
24f17998c8 refactor(website): revise landing page 2023-04-16 19:10:50 +02:00
iCrawl
eda118dae9 fix(guide): optimize redirects on middleware level 2023-04-16 17:42:05 +02:00
iCrawl
f2fce0a7da refactor(guide): always redirect to introduction 2023-04-16 17:38:45 +02:00
Jiralite
29389e39f4 feat(BaseInteraction): Support new channel payload (#9337)
* feat(BaseInteraction): support new channel payload

* refactor(InteractionCreate): different approach

Co-Authored-By: Synbulat Biishev <contact@syjalo.dev>

---------

Co-authored-by: Synbulat Biishev <contact@syjalo.dev>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-16 14:28:32 +00:00
iCrawl
d5e9e36e8a fix(guide): let the breadcrumbs grow to the full size 2023-04-16 13:29:55 +02:00
iCrawl
8c0eb56ba5 fix(guide): ring cutoff on guide header 2023-04-16 13:02:10 +02:00
iCrawl
1b74c774c0 fix(website): cutoff ring outline in breadcrumbs 2023-04-16 11:22:16 +02:00
iCrawl
01949d6e3a fix(website): wrong breakpoint 2023-04-16 00:23:33 +02:00
iCrawl
3ea26cc1a6 refactor(website): only show search box on md and up 2023-04-16 00:17:12 +02:00
Suneet Tipirneni
a516d46f91 fix(website): right-align search bar (#9395) 2023-04-16 00:03:19 +02:00
iCrawl
b35b935679 fix(guide): sidebar types 2023-04-15 23:50:18 +02:00
iCrawl
0928c8f6ff fix(guide): remove astro leftovers 2023-04-15 23:37:19 +02:00
iCrawl
188d5eea28 chore: fix yarn.lock 2023-04-15 23:31:48 +02:00
iCrawl
412e4fffae build(website): prepare for turbopack 2023-04-15 18:15:24 +02:00
iCrawl
5e2f94c591 fix(website): max height 2023-04-15 14:28:20 +02:00
iCrawl
49627b1f46 chore(website): add banner 2023-04-15 14:03:03 +02:00
iCrawl
bcb48fea3e chore(website): redirect to core instead 2023-04-15 13:03:27 +02:00
iCrawl
c87e826087 style(website): remove dynamic width check 2023-04-15 04:43:23 +02:00
Ben
2dddbe1f32 feat(RoleTagData): add guildConnections (#9366)
* feat(roleTagData): add guildConnections

* feat(roletagdata): add guildConnections

* Update packages/discord.js/typings/index.d.ts

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: space <spaceeec@yahoo.com>
2023-04-14 22:04:57 +00:00
Almeida
86e5f5a119 docs(Options): fix links and invalid syntax (#9322)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-14 21:49:51 +00:00
BoogeyMan
300059cb26 docs: cleanup MessageCreateOptions and MessageReplyOptions (#9283)
* Fixed MessageReplyOptions

* updated MessageReplyOptions

* Deduplication - created BaseMessageCreateOptions

* Update packages/discord.js/src/structures/interfaces/TextBasedChannel.js

Co-authored-by: Jaw0r3k <jaworekwiadomosci@gmail.com>

* Fixed spacing

---------

Co-authored-by: Jaw0r3k <jaworekwiadomosci@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-14 21:46:15 +00:00
Erwan
d4c1fecbe2 fix(Message#editable): fix permissions check in locked threads (#9319)
fix(Message#editable): fix permissions check if channel is thread & locked

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-14 21:42:54 +00:00
Jiralite
79bcdfa767 docs(Events): Document auto moderation events (#9342)
docs(Events): document auto moderation events

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-14 21:39:35 +00:00
Jiralite
ca4de2d9c6 fix(BaseSelectMenuBuilder): Modify class to be abstract (#9358)
fix(BaseSelectMenuBuilder): abstraction

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-14 21:34:55 +00:00
Jiralite
794abe8450 refactor: Remove fromInteraction in internal channel creation (#9335)
refactor: remove `fromInteraction`
2023-04-14 21:29:27 +00:00
DD
02dfaf1aa2 refactor: abstract identify throttling and correct max_concurrency handling (#9375)
* refactor: properly support max_concurrency ratelimit keys

* fix: properly block for same key

* chore: export session state

* chore: throttler no longer requires manager

* refactor: abstract throttlers

* chore: proper member order

* chore: remove leftover debug log

* chore: use @link tag in doc comment

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* chore: suggested changes

* fix(WebSocketShard): cancel identify if the shard closed in the meantime

* refactor(throttlers): support abort signals

* fix: memory leak

* chore: remove leftover

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-14 20:26:37 +00:00
Suneet Tipirneni
cac3c07729 fix(website): link dapi-types to proper website (#9388) 2023-04-14 20:53:57 +02:00
iCrawl
83143178aa ci: remove nextjs bundle analysis 2023-04-14 20:10:26 +02:00
iCrawl
615752e32b chore(website): cleanup ts-errors 2023-04-14 20:03:40 +02:00
iCrawl
2c25639a85 chore: update next.js dep 2023-04-14 19:23:59 +02:00
iCrawl
a76c1ddacc chore: fixup build and lint 2023-04-14 17:11:12 +02:00
Jiralite
0d0e4d1cb6 feat: Add changes from v13 to v14 (#9384) 2023-04-14 10:58:23 +02:00
iCrawl
d35f670d7a chore(website): move around static label 2023-04-14 10:33:20 +02:00
iCrawl
e4c5f794b0 fix(website): client-side rendering fallback 2023-04-13 23:29:45 +02:00
Jiralite
88cab1a0ec fix: Update DocsLink usage (#9382) 2023-04-13 22:55:15 +02:00
iCrawl
1c2cc09e05 fix(guide): temporary fix contentlayer dev 2023-04-13 22:28:33 +02:00
Jiralite
2a684361d4 fix(guide): Treeshake react-icons (#9381) 2023-04-13 22:09:40 +02:00
Almeida
66dc4014fe fix(interactions): make data parameter optional (#9379) 2023-04-13 22:01:08 +02:00
Jiralite
39c6694561 feat(DocsLink): Implement the component (#9380)
Co-authored-by: Noel <buechler.noel@outlook.com>
2023-04-13 21:55:15 +02:00
iCrawl
70da3746f8 fix(website): static badge and overload switcher 2023-04-13 20:56:44 +02:00
iCrawl
0340622f1a fix(ui): discord components 2023-04-13 20:15:59 +02:00
iCrawl
f36878677c chore: add chromatic 2023-04-13 19:41:23 +02:00
iCrawl
e5859b41cf chore: storybook 2023-04-13 19:09:56 +02:00
Jiralite
8218ffc78d types(CategoryChannel): Ensure parent and parentId are null (#9327)
types: ensure categories' parents are `null`
2023-04-13 16:10:53 +00:00
Jiralite
7ff3d528d9 types(GuildTextBasedChannel): Remove unnecessary exclusion of forum channels (#9326)
types(GuildTextBasedChannel): remove exclusion of forum channels

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-13 15:55:48 +00:00
Jiralite
0b7f296b62 chore(guide): Update pages (#9372) 2023-04-13 17:10:06 +02:00
Jiralite
85a379fdf8 feat(guide): How to contribute (#9376) 2023-04-13 14:19:43 +02:00
Jiralite
8ffcf77840 docs: add SnowflakeUtil (#9371) 2023-04-13 14:19:13 +02:00
iCrawl
fcb3a1ab27 fix(guide): update return link on 404 2023-04-12 01:04:36 +02:00
iCrawl
40be4f80dc fix(guide): light mode fix 2023-04-12 00:44:21 +02:00
iCrawl
ee907f32f3 feat(guide): sidebar 2023-04-12 00:39:35 +02:00
iCrawl
731ea5f3cb fix(website?): static params 2023-04-11 20:52:37 +02:00
iCrawl
24c462bf6f refactor(website): dark mode 2023-04-11 20:13:59 +02:00
iCrawl
78e02c4b63 fix(website): navigation and 404 2023-04-11 19:19:33 +02:00
iCrawl
dcf8757d35 ci: correct path again 2023-04-11 18:36:13 +02:00
iCrawl
a0c57abadd ci: uploading to before mv 2023-04-11 18:26:27 +02:00
iCrawl
1c5a6fa552 ci: update glob path 2023-04-11 18:12:56 +02:00
Jiralite
6a221a9676 fix(Property): Check for a property type excerpt (#9365) 2023-04-11 14:12:03 +02:00
DD
dcf58d8140 refactor(WebSocketShard): waitForEvent and its error handling (#9282)
* refactor(WebSocketShard): waitForEvent and its error handling

* chore: remove unnecessary error event

* chore: handle ECONNREFUSED/ECONNRESET

* fix: reset network error check

---------

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-11 11:10:32 +00:00
Jiralite
676307ff5c fix(core): Support attachment editing on interactions (#9356)
fix(core): support attachment editing on interactions

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-10 20:35:54 +00:00
Jiralite
23e0ac56f4 docs(formatters): Enhance the documentation (#9364) 2023-04-10 22:29:06 +02:00
Jiralite
8073561824 docs(builders): Add some basic documentation (#9359) 2023-04-10 22:09:51 +02:00
Jiralite
c0f2dd7131 docs: Use stable tag (#9343)
docs: use stable tag
2023-04-10 14:55:51 +00:00
Almeida
d6905b3b97 fix(website): dont show parameters header on functions without parameters (#9361) 2023-04-10 14:00:32 +02:00
Jiralite
774e23c572 fix(Function): Add link to source file (#9360) 2023-04-10 14:00:03 +02:00
Jiralite
1c8567f147 feat: Support @returns (#9362)
Co-authored-by: Noel <buechler.noel@outlook.com>
2023-04-10 13:59:29 +02:00
Jiralite
733c96c255 feat: Support @defaultValue (#9363) 2023-04-10 13:56:43 +02:00
iCrawl
69cdeb7296 ci: fix docs upload path 2023-04-10 13:23:27 +02:00
iCrawl
0019700869 chore(website): switch to revalidate on fetch level 2023-04-10 12:51:13 +02:00
iCrawl
3ea4d26ee9 fix(scripts): skip over namespaces 2023-04-09 19:32:41 +02:00
iCrawl
250eccf118 fix(website): members only on interfaces and classes 2023-04-09 19:12:42 +02:00
iCrawl
7d3827ebd1 fix(website): pass on revalidate 2023-04-09 18:55:59 +02:00
iCrawl
d1955f7c9e feat(website): static, optional, private, inherits, extends 2023-04-09 18:40:49 +02:00
iCrawl
6412da4921 feat: endpoint to retriveve memeber info 2023-04-09 14:21:45 +02:00
Jiralite
79123fb260 fix(ExcerptText): Use resolveItemURI() in excerpts (#9354)
Co-authored-by: Noel <buechler.noel@outlook.com>
2023-04-09 13:23:05 +02:00
iCrawl
3d2f4b405e chore(guide): bring guide up to speed 2023-04-09 12:41:02 +02:00
Almeida
bfee6c8d88 perf(RoleManager): dont call Role#position getter twice per role (#9352)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-09 10:29:18 +00:00
Jiralite
fbbce3eb4b fix: Correct @link tags that involve parents (#9351) 2023-04-09 11:22:07 +02:00
Jiralite
b2eec5f9fc docs: Remove JSONEncondable (#9344)
docs: remove `JSONEncondable`

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-08 21:55:58 +00:00
Jiralite
b618e215f2 fix(util): Remove extra slash (#9347) 2023-04-08 23:51:58 +02:00
Jiralite
d66d113333 docs: Use @link in @see (#9348) 2023-04-08 23:50:58 +02:00
iCrawl
1b9d07f941 chore(website): downgrade planetscale dep 2023-04-07 21:25:15 +02:00
iCrawl
f893c6a357 chore: deps 2023-04-07 21:13:36 +02:00
iCrawl
687bbd889f chore(website): this may solve dynamic route problems
https://github.com/vercel/next.js/pull/47982
2023-04-07 20:54:16 +02:00
iCrawl
f195556b64 fix(website): downgrade nextjs 2023-04-07 20:10:20 +02:00
iCrawl
9aafdd6214 fix(website): model import 2023-04-07 13:41:19 +02:00
Jiralite
e3e7d500dc refactor: Use description constant in landing page (#9334) 2023-04-07 13:12:48 +02:00
iCrawl
3615e2f2d2 chore(website): deps 2023-04-07 13:10:01 +02:00
Tetie
a7425c29c4 fix: fix external links (#9313)
* fix: fix external links

* fix: fix external links

* fix: link to correct classes

* fix: fix JSONEncodable link

* chore: fix select menu link

* fix: fix external links

* fix: link to correct classes

* fix: fix JSONEncodable link

* chore: fix select menu link

* fix: fix collection#sweep url

* fix: fix subpackages urls

* fix: fix discord.js docs url

* Update packages/util/docs/README.md

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Aura Román <kyradiscord@gmail.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-04-07 10:35:56 +00:00
Suneet Tipirneni
c519fe66c9 refactor(ui): add 'use client' directives to client-only components (#9330) 2023-04-07 09:22:40 +02:00
Almeida
24f280290b fix(website): dont crash when serviceWorker is not available (#9331) 2023-04-07 09:22:09 +02:00
Almeida
6912faa9b3 fix(core): missed optional options (#9311)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-04 18:37:26 +00:00
iCrawl
0645bf0f7f fix(website): edge-config fallback 2023-04-02 18:30:00 +02:00
iCrawl
6aba9e99eb ci: planetscale upload 2023-04-02 13:22:52 +02:00
iCrawl
5efdf57894 chore: deps 2023-04-02 12:59:19 +02:00
280 changed files with 9024 additions and 4101 deletions

View File

@@ -7,8 +7,5 @@
"rules": {
"@typescript-eslint/consistent-type-definitions": ["error", "interface"]
},
"ignorePatterns": ["**/dist/*"],
"env": {
"jest": true
}
"ignorePatterns": ["**/dist/*"]
}

27
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,27 @@
# Learn how to add code owners here:
# https://help.github.com/en/articles/about-code-owners
* @iCrawl
/apps/guide/ @discordjs/website @discordjs/guide
/apps/guide/src/content/ @discordjs/guide
/apps/website/ @discordjs/website
/packages/actions/ @discordjs/actions
/packages/api-extractor-utils/ @discordjs/api-extractor-utils
/packages/brokers/ @discordjs/brokers
/packages/builders/ @discordjs/builders
/packages/collection/ @discordjs/collection
/packages/core/ @discordjs/core
/packages/discord.js/ @discordjs/core
/packages/docgen/ @iCrawl
/packages/formatters/ @discordjs/formatters
/packages/next/ @discordjs/core
/packages/proxy/ @discordjs/proxy
/packages/proxy-container/ @discordjs/proxy
/packages/rest/ @discordjs/rest
/packages/scripts/ @discordjs/scripts
/packages/ui/ @discordjs/ui
/packages/util/ @discordjs/util
/packages/voice/ @discordjs/core
/packages/ws/ @discordjs/ws

View File

@@ -1,8 +0,0 @@
addReviewers: true
reviewers:
- iCrawl
- SpaceEEC
- kyranet
- vladfrangu
numberOfReviewers: 0
runOnDraft: true

View File

@@ -129,6 +129,15 @@ jobs:
with:
tag: ${{ github.ref_name }}
- name: Upload documentation to database
if: ${{ github.ref_type == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
uses: ./packages/actions/src/uploadDocumentation
with:
package: ${{ steps.extract-tag.outputs.package }}
version: ${{ steps.extract-tag.outputs.semver }}
- name: Move docs to correct directory
if: ${{ github.ref_type == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
env:
@@ -144,13 +153,12 @@ jobs:
fi
- name: Upload documentation to database
if: ${{ github.ref_type == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
if: ${{ github.ref_type == 'branch' }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
uses: ./packages/actions/src/uploadDocumentation
with:
package: ${{ steps.extract-tag.outputs.package }}
version: ${{ steps.extract-tag.outputs.semver }}
package: ${{ matrix.package }}
- name: Move docs to correct directory
if: ${{ github.ref_type == 'branch' }}
@@ -165,14 +173,6 @@ jobs:
mv docs/${PACKAGE}/docs/docs.api.json out/${PACKAGE}/${GITHUB_REF_NAME}.api.json
fi
- name: Upload documentation to database
if: ${{ github.ref_type == 'branch' }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
uses: ./packages/actions/src/uploadDocumentation
with:
package: ${{ matrix.package }}
- name: Commit and push
run: |
cd out

View File

@@ -1,102 +0,0 @@
name: 'Next.js Bundle Analysis'
on:
push:
branches:
- 'main'
pull_request_target:
paths:
- 'apps/website/**'
workflow_dispatch:
defaults:
run:
working-directory: apps/website
permissions:
contents: read
actions: read
pull-requests: write
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install node.js v16
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install dependencies
uses: ./packages/actions/src/yarnCache
- name: Restore next build
uses: actions/cache@v3
id: restore-build-cache
env:
cache-name: cache-next-build
with:
path: apps/website/.next/cache
key: ${{ runner.os }}-build-${{ env.cache-name }}
- name: Build packages
run: yarn run --top-level build
- name: Build website
run: yarn workspace @discordjs/website run build:local
- name: Analyze bundle
run: npx -yes -p github:hashicorp/nextjs-bundle-analysis report
- name: Upload bundle
uses: actions/upload-artifact@v3
with:
name: bundle
path: apps/website/.next/analyze/__bundle_analysis.json
- name: Download base branch bundle stats
uses: dawidd6/action-download-artifact@v2
if: success() && github.event.number
with:
workflow: nextjs-bundle-analysis.yml
commit: ${{ github.event.pull_request.base.sha }}
path: apps/website/.next/analyze/base
- name: Compare with base branch bundle
if: success() && github.event.number
run: ls -laR .next/analyze/base && npx -yes -p github:hashicorp/nextjs-bundle-analysis compare
- name: Get comment body
id: get-comment-body
if: success() && github.event.number
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const fs = require('fs');
const comment = fs.readFileSync('apps/website/.next/analyze/__bundle_analysis_comment.txt', 'utf8');
core.setOutput('body', comment);
- name: Find Comment
uses: peter-evans/find-comment@v2
if: success() && github.event.number
id: fc
with:
issue-number: ${{ github.event.number }}
body-includes: '<!-- __NEXTJS_BUNDLE -->'
- name: Create Comment
uses: peter-evans/create-or-update-comment@v2
if: success() && github.event.number && steps.fc.outputs.comment-id == 0
with:
issue-number: ${{ github.event.number }}
body: ${{ steps.get-comment-body.outputs.body }}
- name: Update Comment
uses: peter-evans/create-or-update-comment@v2
if: success() && github.event.number && steps.fc.outputs.comment-id != 0
with:
issue-number: ${{ github.event.number }}
body: ${{ steps.get-comment-body.outputs.body }}
comment-id: ${{ steps.fc.outputs.comment-id }}
edit-mode: replace

View File

@@ -11,7 +11,3 @@ jobs:
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
sync-labels: true
- name: Automatically assign reviewers
if: github.event.action == 'opened'
uses: kentaro-m/auto-assign-action@v1.2.3

View File

@@ -1,5 +1,5 @@
{
"*": "prettier --ignore-unknown --write",
"{src/**,__tests__/**}.{mjs,js,cjs,ts,tsx,astro}": "eslint --ext .mjs,.js,.cjs,.ts,.tsx,.astro --fix",
"{src/**,__tests__/**}.{mjs,js,cjs,ts,tsx}": "eslint --ext .mjs,.js,.cjs,.ts,.tsx --fix",
"src/**.ts": "vitest related --run --config ../../vitest.config.ts"
}

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
CODEOWNERS

View File

@@ -9,7 +9,6 @@
"christian-kohler.npm-intellisense",
"christian-kohler.path-intellisense",
"antfu.unocss",
"astro-build.astro-vscode",
"unifiedjs.vscode-mdx"
]
}

View File

@@ -1,7 +1,6 @@
{
"eslint.workingDirectories": [{ "pattern": "./apps/*" }, { "pattern": "./packages/*" }],
"eslint.validate": ["javascript", "javascriptreact", "astro", "typescript", "typescriptreact"],
"prettier.documentSelectors": ["**/*.astro"],
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {

View File

@@ -11,3 +11,8 @@ plugins:
spec: 'https://github.com/Dcard/yarn-plugins/releases/latest/download/plugin-docker-build.js'
yarnPath: .yarn/releases/yarn-3.5.0.cjs
packageExtensions:
'@storybook/core-common@*':
dependencies:
'@storybook/react-vite': '7.0.5'

View File

@@ -1,5 +1,5 @@
{
"extends": ["../../.eslintrc.json", "neon/react", "neon/next", "neon/edge", "neon/prettier"],
"extends": ["../../.eslintrc.json", "neon/react", "neon/next", "neon/edge", "@unocss", "neon/prettier"],
"settings": {
"react": {
"version": "detect"

View File

@@ -1,7 +1 @@
module.exports = {
...require('../../.prettierrc.json'),
plugins: [
'prettier-plugin-tailwindcss', // MUST come last
],
pluginSearchDirs: false,
};
module.exports = require('../../.prettierrc.json');

View File

@@ -6,6 +6,7 @@ import { defineDocumentType, makeSource } from 'contentlayer/source-files';
// import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypeSlug from 'rehype-slug';
import remarkGfm from 'remark-gfm';
import codeHikeThemeDarkPlus from './src/styles/code-hike-theme-dark-plus.json';
export const Content = defineDocumentType(() => ({
name: 'Content',
@@ -16,21 +17,21 @@ export const Content = defineDocumentType(() => ({
type: 'string',
required: true,
},
summary: {
type: 'string',
},
image: {
category: {
type: 'string',
required: true,
},
},
computedFields: {
slug: {
type: 'string',
resolve: (doc) => doc._raw.flattenedPath,
// eslint-disable-next-line unicorn/prefer-string-replace-all
resolve: (doc) => doc._raw.flattenedPath.replace(/\d+-/g, ''),
},
url: {
type: 'string',
resolve: (post) => `/posts/${post._raw.flattenedPath}`,
// eslint-disable-next-line unicorn/prefer-string-replace-all
resolve: (doc) => `/guide/${doc._raw.flattenedPath.replace(/\d+-/g, '')}`,
},
},
}));
@@ -67,7 +68,7 @@ export default makeSource({
contentDirPath: 'src/content',
documentTypes: [Content],
mdx: {
remarkPlugins: [remarkGfm, [remarkCodeHike, { theme: 'css-variables', lineNumbers: true }]],
remarkPlugins: [remarkGfm, [remarkCodeHike, { theme: codeHikeThemeDarkPlus, lineNumbers: true }]],
rehypePlugins: [
rehypeSlug,
// [
@@ -75,7 +76,7 @@ export default makeSource({
// {
// properties: {
// class:
// 'relative inline-flex w-6 h-6 place-items-center place-content-center outline-0 text-black dark:text-white ml-2',
// 'relative inline-flex w-6 h-6 place-items-center place-content-center outline-none text-black dark:text-white ml-2',
// },
// behavior: 'after',
// group: async ({ tagName }: { tagName: string }) =>

View File

@@ -1,7 +1,7 @@
// import { fileURLToPath } from 'node:url';
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-require-imports */
// import bundleAnalyzer from '@next/bundle-analyzer';
// import { withContentlayer } from 'next-contentlayer';
const { fileURLToPath } = require('node:url');
const bundleAnalyzer = require('@next/bundle-analyzer');
const { withContentlayer } = require('next-contentlayer');
@@ -9,19 +9,11 @@ const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withContentlayer(
withBundleAnalyzer({
module.exports = withBundleAnalyzer(
withContentlayer({
reactStrictMode: true,
eslint: {
ignoreDuringBuilds: true,
},
// Until Next.js fixes their type issues
typescript: {
ignoreBuildErrors: true,
},
experimental: {
appDir: true,
fallbackNodePolyfills: false,
},
images: {
dangerouslyAllowSVG: true,

View File

@@ -12,9 +12,10 @@
"build:css": "yarn generate:css",
"build:analyze": "cross-env-shell ANALYZE=true yarn build:prod",
"preview": "next start",
"dev": "concurrently 'yarn dev:css' 'yarn dev:next'",
"dev": "concurrently 'yarn dev:contentlayer' 'yarn dev:css' 'yarn dev:next'",
"dev:next": "next dev",
"dev:css": "yarn generate:css --watch",
"dev:contentlayer": "contentlayer dev",
"generate:css": "unocss 'src/**/*.tsx' '../../packages/ui/src/lib/components/**/*.tsx' --out-file ./src/styles/unocss.css --config ../../unocss.config.ts",
"lint": "prettier --check . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --format=pretty",
"format": "prettier --write . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --fix --format=pretty",
@@ -43,57 +44,56 @@
},
"homepage": "https://discord.js.org",
"dependencies": {
"@code-hike/mdx": "^0.7.5-next.0",
"@code-hike/mdx": "^0.8.2",
"@discordjs/ui": "workspace:^",
"@react-icons/all-files": "^4.1.0",
"@vercel/analytics": "^0.1.11",
"@vercel/edge-config": "^0.1.5",
"@vercel/og": "^0.5.0",
"ariakit": "^2.0.0-next.43",
"@vercel/edge-config": "^0.1.7",
"@vercel/og": "^0.5.2",
"ariakit": "^2.0.0-next.44",
"cmdk": "^0.2.0",
"contentlayer": "^0.3.1",
"next": "^13.2.4",
"next": "^13.3.0",
"next-contentlayer": "^0.3.1",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-custom-scrollbars-2": "^4.5.0",
"react-dom": "^18.2.0",
"react-icons": "^4.8.0",
"react-use": "^17.4.0",
"rehype-autolink-headings": "^6.1.1",
"rehype-ignore": "^1.0.4",
"rehype-ignore": "^1.0.5",
"rehype-raw": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark-gfm": "^3.0.1",
"server-only": "^0.0.1",
"sharp": "^0.32.0"
},
"devDependencies": {
"@next/bundle-analyzer": "^13.2.4",
"@next/bundle-analyzer": "^13.3.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/node": "18.15.11",
"@types/react": "^18.0.31",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"@unocss/cli": "^0.50.6",
"@unocss/reset": "^0.50.6",
"@unocss/cli": "^0.51.4",
"@unocss/eslint-config": "^0.51.4",
"@unocss/reset": "^0.51.4",
"@vitejs/plugin-react": "^3.1.0",
"@vitest/coverage-c8": "^0.29.8",
"@vitest/coverage-c8": "^0.30.1",
"concurrently": "^8.0.1",
"cross-env": "^7.0.3",
"eslint": "^8.37.0",
"eslint-config-neon": "^0.1.41",
"eslint": "^8.38.0",
"eslint-config-neon": "^0.1.42",
"eslint-formatter-pretty": "^5.0.0",
"happy-dom": "^8.9.0",
"happy-dom": "^9.7.1",
"hast-util-to-string": "^2.0.0",
"hastscript": "^7.2.0",
"html-escaper": "^3.0.3",
"lighthouse": "^10.1.0",
"lighthouse": "^10.1.1",
"prettier": "^2.8.7",
"prettier-plugin-astro": "^0.8.0",
"prettier-plugin-tailwindcss": "^0.2.6",
"typescript": "^5.0.3",
"unocss": "^0.50.6",
"vercel": "^28.18.3",
"typescript": "^5.0.4",
"unocss": "^0.51.4",
"vercel": "^28.18.5",
"vitest": "^0.29.8"
},
"engines": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -4,7 +4,7 @@ export default function Error({ error }: { error: Error }) {
console.error(error);
return (
<div className="mx-auto flex h-full max-w-lg flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">500</h1>
<h2 className="text-[2rem] md:text-[3rem]">Error.</h2>
</div>

View File

@@ -8,10 +8,10 @@ export default function GlobalError({ error }: { error: Error }) {
return (
<html className={inter.variable} lang="en" suppressHydrationWarning>
<body className="dark:bg-dark-800 bg-light-600">
<body className="bg-light-600 dark:bg-dark-600 dark:text-light-900">
<Providers>
<main className="mx-auto h-screen max-w-2xl">
<div className="mx-auto flex h-screen max-w-lg flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<main className="mx-auto max-w-2xl min-h-screen">
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">500</h1>
<h2 className="text-[2rem] md:text-[3rem]">Error.</h2>
</div>

View File

@@ -0,0 +1 @@
export { default } from '~/app/not-found';

View File

@@ -3,7 +3,7 @@ import { notFound } from 'next/navigation';
import { Mdx } from '~/components/Mdx';
export async function generateStaticParams() {
return allContents.map((content) => ({ slug: content.slug }));
return allContents.map((content) => ({ slug: [content.slug] }));
}
export default function Page({ params }: { params: { slug: string[] } }) {
@@ -14,7 +14,7 @@ export default function Page({ params }: { params: { slug: string[] } }) {
}
return (
<article className="prose mx-auto max-w-4xl py-8">
<article className="max-w-none prose">
<Mdx code={content?.body.code ?? ''} />
</article>
);

View File

@@ -0,0 +1,25 @@
import type { PropsWithChildren } from 'react';
import { Providers } from './providers';
import Footer from '~/components/Footer';
import Header from '~/components/Header';
import { Nav } from '~/components/Nav';
export default function Layout({ children }: PropsWithChildren) {
return (
<Providers>
<main className="mx-auto max-w-7xl px-4 lg:max-w-full">
<Header />
<div className="relative top-6 mx-auto max-w-7xl gap-6 lg:max-w-full lg:flex">
<div className="lg:sticky lg:top-23 lg:h-[calc(100vh_-_105px)]">
<Nav />
</div>
<div className="mx-auto max-w-5xl min-w-xs w-full pb-10">
{children}
<Footer />
</div>
</div>
</main>
</Providers>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return null;
}

View File

@@ -0,0 +1,8 @@
'use client';
import type { PropsWithChildren } from 'react';
import { NavProvider } from '~/contexts/nav';
export function Providers({ children }: PropsWithChildren) {
return <NavProvider>{children}</NavProvider>;
}

View File

@@ -1,5 +1,5 @@
import { Analytics } from '@vercel/analytics/react';
import type { Metadata } from 'next/types';
import type { Metadata } from 'next';
import type { PropsWithChildren } from 'react';
import { Providers } from './providers';
import { DESCRIPTION } from '~/util/constants';
@@ -44,7 +44,10 @@ export const metadata: Metadata = {
manifest: '/site.webmanifest',
themeColor: '#5865f2',
themeColor: [
{ media: '(prefers-color-scheme: light)', color: '#f1f3f5' },
{ media: '(prefers-color-scheme: dark)', color: '#181818' },
],
colorScheme: 'light dark',
appleWebApp: {
@@ -74,7 +77,7 @@ export const metadata: Metadata = {
export default function RootLayout({ children }: PropsWithChildren) {
return (
<html className={`${inter.variable} ${jetBrainsMono.variable}`} lang="en" suppressHydrationWarning>
<body className="dark:bg-dark-800 bg-light-600">
<body className="bg-light-600 dark:bg-dark-600 dark:text-light-900">
<Providers>{children}</Providers>
<Analytics />
</body>

View File

@@ -1,6 +1,6 @@
export default function Loading() {
return (
<div className="mx-4 flex min-h-screen flex-col items-center justify-center gap-4">
<div className="mx-4 min-h-screen flex flex-col items-center justify-center gap-4">
<svg
className="h-9 w-9 animate-spin text-black dark:text-white"
fill="none"

View File

@@ -2,12 +2,12 @@ import Link from 'next/link';
export default function NotFound() {
return (
<div className="mx-auto flex min-h-screen max-w-lg flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">404</h1>
<h2 className="text-[2rem] md:text-[3rem]">Not found.</h2>
<Link
className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded border-0 px-6 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px"
href="/docs/packages"
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-blurple px-6 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href="/guide"
>
Take me back
</Link>

View File

@@ -1,26 +1,3 @@
import Image from 'next/image';
import vercelLogo from '~/assets/powered-by-vercel.svg';
export default function Page() {
return (
<div className="mx-auto flex min-h-screen max-w-6xl flex-col place-items-center gap-12 px-8 py-16 lg:place-content-center lg:px-8 lg:py-0">
<div className="flex flex-row place-content-center">
<a
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
rel="noopener noreferrer"
target="_blank"
title="Vercel"
>
<Image
alt="Vercel"
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAABLCAQAAAA1k5H2AAAAi0lEQVR42u3SMQEAAAgDoC251a3gL2SgmfBYBRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAgwWEOSWBnYbKggAAAABJRU5ErkJggg=="
placeholder="blur"
priority
src={vercelLogo}
/>
</a>
</div>
</div>
);
return null;
}

View File

@@ -1,3 +1,79 @@
export function DocsLink() {
return null;
import { FiExternalLink } from '@react-icons/all-files/fi/FiExternalLink';
import { BASE_URL, BASE_URL_LEGACY, PACKAGES, VERSION } from '~/util/constants';
interface DocsLinkOptions {
/**
* Whether to apply brackets to the end of the symbol to denote a method.
*
* @remarks Functions automatically infer this.
*/
brackets?: boolean;
/**
* The package.
*
* @defaultValue `'discord.js'`
*/
package?: (typeof PACKAGES)[number];
/**
* The initial documentation class, function, interface etc.
*
* @example `'Client'`
*/
parent: string;
/**
* Whether to reference a static property.
*
* @remarks
* This should only be used for the https://discord.js.org domain
* as static properties are not identified in the URL.
*/
static?: boolean;
/**
* The symbol belonging to the parent.
*
* @example '`login'`
*/
symbol?: string;
/**
* The type of the {@link DocsLinkOptions.parent}.
*
* @example `'class'`
* @example `'Function'`
*/
type: string;
}
export function DocsLink({
package: docs = PACKAGES[0],
type,
parent,
symbol,
brackets,
static: staticReference,
}: DocsLinkOptions) {
const bracketText = brackets || type.toUpperCase() === 'FUNCTION' ? '()' : '';
const trimmedSymbol = symbol;
let url;
let text;
if (docs === PACKAGES[0]) {
url = `${BASE_URL_LEGACY}/${VERSION}/${type}/${parent}`;
if (trimmedSymbol) url += `?scrollTo=${trimmedSymbol}`;
text = `${parent}${trimmedSymbol ? (trimmedSymbol.startsWith('s-') ? '.' : '#') : ''}${
// eslint-disable-next-line prefer-named-capture-group
trimmedSymbol ? `${trimmedSymbol.replace(/(e|s)-/, '')}` : ''
}${bracketText}`;
} else {
url = `${BASE_URL}/${docs}/stable/${parent}:${type}`;
if (trimmedSymbol) url += `#${trimmedSymbol}`;
text = `${parent}${trimmedSymbol ? `${staticReference ? '.' : '#'}${trimmedSymbol}` : ''}${bracketText}`;
}
return (
<a className="inline-flex flex-row place-items-center gap-1" href={url} rel="noopener noreferrer" target="_blank">
{text}
<FiExternalLink size={18} />
</a>
);
}

View File

@@ -1,10 +0,0 @@
import { FiExternalLink } from 'react-icons/fi';
export function ExternalLink({ href, title }: { href: string; title: string }) {
return (
<a className="text-blurple inline-flex place-items-center gap-2 text-sm font-semibold" href={href}>
<p>{title}</p>
<FiExternalLink size={18} />
</a>
);
}

View File

@@ -0,0 +1,81 @@
import Image from 'next/image';
import vercelLogo from '~/assets/powered-by-vercel.svg';
export default function Footer() {
return (
<footer className="md:pl-12 md:pr-12">
<div className="mx-auto max-w-6xl flex flex-col place-items-center gap-12 pt-12 lg:place-content-center">
<div className="w-full flex flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
rel="external noopener noreferrer"
target="_blank"
title="Vercel"
>
<Image
alt="Vercel"
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAABLCAQAAAA1k5H2AAAAi0lEQVR42u3SMQEAAAgDoC251a3gL2SgmfBYBRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAgwWEOSWBnYbKggAAAABJRU5ErkJggg=="
height={44}
placeholder="blur"
src={vercelLogo}
width={212}
/>
</a>
<div className="flex flex-row gap-6 md:gap-12">
<div className="flex flex-col gap-2">
<div className="text-lg font-semibold">Community</div>
<div className="flex flex-col gap-1">
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://discord.gg/djs"
rel="external noopener noreferrer"
target="_blank"
>
Discord
</a>
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://github.com/discordjs/discord.js/discussions"
rel="external noopener noreferrer"
target="_blank"
>
GitHub discussions
</a>
</div>
</div>
<div className="flex flex-col gap-2">
<div className="text-lg font-semibold">Project</div>
<div className="flex flex-col gap-1">
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://github.com/discordjs/discord.js"
rel="external noopener noreferrer"
target="_blank"
>
discord.js
</a>
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://discord.js.org/docs"
rel="noopener noreferrer"
target="_blank"
>
discord.js documentation
</a>
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://discord-api-types.dev"
rel="external noopener noreferrer"
target="_blank"
>
discord-api-types
</a>
</div>
</div>
</div>
</div>
</div>
</footer>
);
}

View File

@@ -0,0 +1,91 @@
'use client';
import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted';
import { VscMenu } from '@react-icons/all-files/vsc/VscMenu';
import { Button } from 'ariakit/button';
import dynamic from 'next/dynamic';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Fragment, useMemo } from 'react';
import { useNav } from '~/contexts/nav';
const ThemeSwitcher = dynamic(async () => import('./ThemeSwitcher'));
export default function Header() {
const pathname = usePathname();
const { setOpened } = useNav();
const pathElements = useMemo(
() =>
pathname
.split('/')
.slice(1)
.map((path, idx, original) => (
<Link
className="rounded outline-none hover:underline focus:ring focus:ring-width-2 focus:ring-blurple"
href={`/${original.slice(0, idx + 1).join('/')}`}
key={`${path}-${idx}`}
>
{path}
</Link>
)),
[pathname],
);
const breadcrumbs = useMemo(
() =>
pathElements.flatMap((el, idx, array) => {
if (idx === 0) {
return (
<Fragment key={`${el.key}-${idx}`}>
<div className="mx-2">/</div>
<div>{el}</div>
<div className="mx-2">/</div>
</Fragment>
);
}
if (idx !== array.length - 1) {
return (
<Fragment key={`${el.key}-${idx}`}>
<div>{el}</div>
<div className="mx-2">/</div>
</Fragment>
);
}
return <div key={`${el.key}-${idx}`}>{el}</div>;
}),
[pathElements],
);
return (
<header className="sticky top-4 z-20 border border-light-900 rounded-md bg-white/75 shadow backdrop-blur-md dark:border-dark-100 dark:bg-dark-600/75">
<div className="block h-16 px-6">
<div className="h-full flex flex-row place-content-between place-items-center gap-8">
<Button
aria-label="Menu"
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none lg:hidden active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
onClick={() => setOpened((open) => !open)}
>
<VscMenu size={24} />
</Button>
<div className="hidden lg:flex lg:grow lg:flex-row lg:overflow-hidden">{breadcrumbs}</div>
<div className="flex flex-row place-items-center gap-4">
<Button
aria-label="GitHub"
as="a"
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded rounded-full bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://github.com/discordjs/discord.js"
rel="external noopener noreferrer"
target="_blank"
>
<VscGithubInverted size={24} />
</Button>
<ThemeSwitcher />
</div>
</div>
</div>
</header>
);
}

View File

@@ -0,0 +1,30 @@
'use client';
import { Scrollbars } from 'react-custom-scrollbars-2';
import { Sidebar } from './Sidebar';
import { useNav } from '~/contexts/nav';
export function Nav() {
const { opened } = useNav();
return (
<nav
className={`dark:bg-dark-600/75 dark:border-dark-100 border-light-900 top-22 fixed bottom-4 left-4 right-4 z-20 mx-auto max-w-5xl rounded-md border bg-white/75 shadow backdrop-blur-md ${
opened ? 'block' : 'hidden'
} lg:min-w-xs lg:sticky lg:block lg:h-full lg:w-full lg:max-w-xs`}
>
<Scrollbars
autoHide
className="[&>div]:overscroll-none"
hideTracksWhenNotNeeded
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
renderTrackVertical={(props) => (
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
)}
universal
>
<Sidebar />
</Scrollbars>
</nav>
);
}

View File

@@ -1,70 +0,0 @@
import { Button } from 'ariakit/button';
import { useState, useEffect } from 'react';
import { FiCommand } from 'react-icons/fi';
import { VscColorMode, VscGithubInverted, VscMenu, VscSearch } from 'react-icons/vsc';
import { useMedia } from 'react-use';
import { Sidebar } from './Sidebar.jsx';
import type { MDXPage } from './SidebarItems.jsx';
export function Navbar({ pages }: { pages?: MDXPage[] | undefined }) {
const matches = useMedia('(min-width: 992px)', false);
const [opened, setOpened] = useState(false);
useEffect(() => {
if (matches) {
setOpened(false);
}
}, [matches]);
return (
<>
<header className="dark:bg-dark-400 dark:border-dark-100 bg-light-600 border-light-800 fixed left-0 top-0 z-20 w-full border-b">
<div className="block h-16 px-6">
<div className="flex h-full flex-row place-content-between place-items-center">
<Button
aria-label="Menu"
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px lg:hidden"
onClick={() => setOpened((open) => !open)}
>
<VscMenu size={24} />
</Button>
<div className="hidden md:flex md:flex-row">Placeholder</div>
<div className="flex flex-row place-items-center gap-4">
<Button
as="div"
className="dark:bg-dark-800 focus:ring-width-2 focus:ring-blurple rounded bg-white px-4 py-2.5 outline-0 focus:ring"
// onClick={() => dialog?.toggle()}
>
<div className="flex flex-row place-items-center gap-4">
<VscSearch size={18} />
<span className="opacity-65">Search...</span>
<div className="opacity-65 flex flex-row place-items-center gap-2">
<FiCommand size={18} /> K
</div>
</div>
</Button>
<Button
aria-label="GitHub"
as="a"
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded rounded-full border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px"
href="https://github.com/discordjs/discord.js"
rel="noopener noreferrer"
target="_blank"
>
<VscGithubInverted size={24} />
</Button>
<Button
aria-label="Toggle theme"
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded rounded-full border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px"
// onClick={() => toggleTheme()}
>
<VscColorMode size={24} />
</Button>
</div>
</div>
</div>
</header>
<Sidebar opened={opened} pages={pages} />
</>
);
}

View File

@@ -1,22 +1,19 @@
import type { MarkdownHeading } from 'astro';
import { useEffect, useMemo, useState } from 'react';
import { useMemo, useState } from 'react';
import { Scrollbars } from 'react-custom-scrollbars-2';
import { VscListSelection } from 'react-icons/vsc';
import { useLocation } from 'react-use';
const LINK_HEIGHT = 30;
const INDICATOR_SIZE = 10;
const INDICATOR_OFFSET = (LINK_HEIGHT - INDICATOR_SIZE) / 2;
export function Outline({ headings }: { headings: MarkdownHeading[] }) {
const state = useLocation();
const [active, setActive] = useState(0);
export function Outline({ headings }: { headings: any[] }) {
// eslint-disable-next-line react/hook-use-state
const [active /* setActive */] = useState(0);
const headingItems = useMemo(
() =>
headings.map((heading, idx) => (
<a
className={`dark:border-dark-100 border-light-800 pl-6.5 focus:ring-width-2 focus:ring-blurple ml-[10px] border-l p-[5px] text-sm outline-0 focus:rounded focus:border-0 focus:ring ${
className={`dark:border-dark-100 border-light-800 pl-6.5 focus:ring-width-2 focus:ring-blurple ml-[10px] border-l p-[5px] text-sm outline-none focus:rounded focus:border-0 focus:ring ${
idx === active
? 'bg-blurple text-white'
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
@@ -32,18 +29,18 @@ export function Outline({ headings }: { headings: MarkdownHeading[] }) {
[headings, active],
);
useEffect(() => {
const idx = headings.findIndex((heading) => heading.slug === state.hash?.slice(1));
if (idx >= 0) {
setActive(idx);
}
}, [state, headings]);
// useEffect(() => {
// const idx = headings.findIndex((heading) => heading.slug === state.hash?.slice(1));
// if (idx >= 0) {
// setActive(idx);
// }
// }, [state, headings]);
return (
<Scrollbars
autoHide
hideTracksWhenNotNeeded
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
renderTrackVertical={(props) => (
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
)}
@@ -51,13 +48,13 @@ export function Outline({ headings }: { headings: MarkdownHeading[] }) {
>
<div className="flex flex-col break-all p-3 pb-8">
<div className="ml-2 mt-4 flex flex-row gap-2">
<VscListSelection size={25} />
{/* <VscListSelection size={25} /> */}
<span className="font-semibold">Contents</span>
</div>
<div className="ml-2 mt-4 flex flex-col gap-2">
<div className="relative flex flex-col">
<div
className="bg-blurple absolute h-[10px] w-[10px] rounded-full border-2 border-black dark:border-white"
className="absolute h-[10px] w-[10px] border-2 border-black rounded-full bg-blurple dark:border-white"
style={{
left: INDICATOR_SIZE / 2 + 0.5,
transform: `translateY(${active * LINK_HEIGHT + INDICATOR_OFFSET}px)`,

View File

@@ -1,7 +1,7 @@
export function PageButton({ url, title, direction }: { direction: 'next' | 'prev'; title: string; url: string }) {
return (
<a
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple flex transform-gpu cursor-pointer select-none appearance-none flex-row flex-col place-items-center gap-2 rounded px-4 py-3 leading-none no-underline outline-0 focus:ring active:translate-y-px"
className="flex flex-row flex-col transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 rounded bg-light-600 px-4 py-3 leading-none no-underline outline-none active:translate-y-px active:bg-light-800 dark:bg-dark-600 hover:bg-light-700 focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-400 dark:hover:bg-dark-500"
href={url}
>
<h3 className="text-md font-semibold">{title}</h3>

View File

@@ -0,0 +1,8 @@
'use client';
import { Section as DJSSection, type SectionOptions } from '@discordjs/ui';
import type { PropsWithChildren } from 'react';
export function Section(options: PropsWithChildren<SectionOptions>) {
return <DJSSection {...options} />;
}

View File

@@ -1,24 +1,62 @@
import { Scrollbars } from 'react-custom-scrollbars-2';
import type { MDXPage } from './SidebarItems.jsx';
'use client';
import { allContents } from 'contentlayer/generated';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Section } from './Section';
import { useNav } from '~/contexts/nav';
const items = allContents.map((content) => ({
title: content.title,
category: content.category,
slug: content.slug,
href: content.url,
}));
function transformItemsByCategory(allContents: typeof items) {
return allContents.reduce<Record<string, typeof items>>((accumulator: any, content) => {
if (!accumulator[content.category]) {
accumulator[content.category] = [];
}
accumulator[content.category].push(content);
return accumulator;
}, {});
}
const itemsByCategory = transformItemsByCategory(items);
export function Sidebar() {
const pathname = usePathname();
const { setOpened } = useNav();
export function Sidebar({ pages, opened }: { opened: boolean; pages?: MDXPage[] | undefined }) {
return (
<nav
className={`h-[calc(100vh - 65px)] dark:bg-dark-600 dark:border-dark-100 border-light-800 fixed bottom-0 left-0 top-[65px] z-20 w-full border-r bg-white ${
opened ? 'block' : 'hidden'
} lg:w-76 lg:max-w-76 lg:block`}
>
<Scrollbars
autoHide
hideTracksWhenNotNeeded
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
renderTrackVertical={(props) => (
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
)}
universal
>
{pages ?? null}
</Scrollbars>
</nav>
<div className="flex flex-col gap-3 p-3">
{Object.keys(itemsByCategory).map((category, idx) => (
<Section
buttonClassName="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-none focus:ring"
key={`${category}-${idx}`}
title={category}
>
{itemsByCategory[category]?.map((member, index) => (
<Link
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-none focus:rounded focus:border-0 focus:ring ${
decodeURIComponent(pathname ?? '') === member.href
? 'bg-blurple text-white'
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
}`}
href={member.href}
key={`${member.title}-${index}`}
onClick={() => setOpened(false)}
title={member.title}
>
<div className="flex flex-row place-items-center gap-2 lg:text-sm">
<span className="truncate">{member.title}</span>
</div>
</Link>
))}
</Section>
))}
</div>
);
}

View File

@@ -1,51 +0,0 @@
import { Section } from '@discordjs/ui';
import type { MDXInstance } from 'astro';
import { useEffect, useMemo, useState } from 'react';
import { useLocation } from 'react-use';
export type MDXPage = MDXInstance<{ category: string; title: string }>;
export function SidebarItems({ pages }: { pages: MDXPage[] }) {
const state = useLocation();
const [active, setActive] = useState<string | undefined>('');
const categories = useMemo(
() =>
pages.reduce<Record<string, MDXPage[]>>((acc, page) => {
if (acc[page.frontmatter.category]) {
acc[page.frontmatter.category]?.push(page);
} else {
acc[page.frontmatter.category] = [page];
}
return acc;
}, {}),
[pages],
);
useEffect(() => {
setActive(state.pathname);
}, [state]);
return Object.keys(categories).map((category, idx) => (
<Section key={`${category}-${idx}`} title={category}>
{categories[category]?.map((member, index) => (
<a
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-0 focus:rounded focus:border-0 focus:ring ${
(member.url || '/') === active
? 'bg-blurple text-white'
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
}`}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
href={member.url || '/'}
key={`${member.frontmatter.title}-${index}}`}
title={member.frontmatter.title}
>
<div className="flex flex-row place-items-center gap-2 lg:text-sm">
<span className="truncate">{member.frontmatter.title}</span>
</div>
</a>
)) ?? null}
</Section>
));
}

View File

@@ -0,0 +1,20 @@
'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 (
<Button
aria-label="Toggle theme"
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded rounded-full bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
onClick={() => toggleTheme()}
>
<VscColorMode size={24} />
</Button>
);
}

View File

@@ -9,14 +9,13 @@ category: Home
<DiscordMessage
interaction={{
author: {
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
avatar: '/assets/old-guide.png',
username: 'discord.js',
},
command: 'upgrade',
command: '/upgrade',
}}
author={{
avatar: 'https://cdn.discordapp.com/avatars/474807795183648809/7f239a0776ff928b2182906a2b3743c9.webp?size=160',
avatar: '/assets/discordjs.png',
bot: true,
username: 'Guide Bot',
time: 'Today at 21:00',
@@ -30,14 +29,11 @@ category: Home
## Site
- Upgraded to [VuePress v2](https://v2.vuepress.vuejs.org/)
- New theme made to match the [discord.js documentation site](https://discord.js.org/)
- Discord message components upgraded to [@discord-message-components/vue](https://github.com/Danktuary/discord-message-components/blob/main/packages/vue/README.md)
- Many fixes in code blocks, grammar, consistency, etc.
We have moved from VuePress to [Next.js](https://nextjs.org/)! The source can be found [here](https://github.com/discordjs/discord.js/tree/main/apps/guide).
## Pages
All content has been updated to use discord.js v14 syntax. The v13 version of the guide can be found at [https://v13.discordjs.guide/](https://v13.discordjs.guide/).
All content has been updated to use discord.js v14 syntax. The v13 version of the guide can be found at https://v13.discordjs.guide.
### New
@@ -54,13 +50,13 @@ All content has been updated to use discord.js v14 syntax. The v13 version of th
- [Voice](/voice/): Rewritten to use the [_`@discordjs/voice`_](https://github.com/discordjs/discord.js/tree/main/packages/voice) package
- [Command handling](/creating-your-bot/command-handling.md/): Updated to use slash commands
- Obsolete sections removed
- _`client.on('message')`_ snippets updated to _`client.on('interactionCreate')`_
- [Message content will become a new privileged intent on August 31, 2022](https://support-dev.discord.com/hc/en-us/articles/4404772028055)
- _`client.on('message')`_ snippets updated to _`client.on(Events.InteractionCreate)`_
- [Message content became a privileged intent on August 31, 2022](https://support-dev.discord.com/hc/articles/4404772028055)
<DiscordMessages rounded>
<DiscordMessage
author={{
avatar: 'https://cdn.discordapp.com/avatars/474807795183648809/7f239a0776ff928b2182906a2b3743c9.webp?size=160',
avatar: '/assets/discordjs.png',
bot: true,
username: 'Guide Bot',
time: 'Today at 21:00',

View File

@@ -0,0 +1,177 @@
---
title: How to contribute
category: Home
---
# How to contribute
Since this guide is made specifically for the discord.js community, we want to be sure to provide the most relevant and up-to-date content. We will, of course, make additions to the current pages and add new ones as we see fit, but fulfilling requests is how we know we're providing content you all want the most.
Requests may be as simple as "add an example to the [frequently asked questions](/popular-topics/faq.html) page", or as elaborate as "add a page regarding [sharding](/sharding/)". We'll do our best to fulfill all requests, as long as they're reasonable.
To make a request, simply head over to [the repository's issue tracker](https://github.com/discordjs/discord.js/issues) and [create a new issue](https://github.com/discordjs/discord.js/issues/new)! Title it appropriately, and let us know exactly what you mean inside the issue description. Make sure that you've looked around the site before making a request; what you want to request might already exist!
<Alert title="Tip" type="success">
Remember that you can always [fork the repository](https://github.com/discordjs/discord.js/fork) and [make a pull
request](https://github.com/discordjs/discord.js/pulls) if you want to add anything to the guide yourself!
</Alert>
We'll also get into some of the more advanced features this guide does below.
## Components
Throughout the guide, you'll see some components from the _`@discordjs/ui`_ package:
- _`Alert`_
- _`Section`_
- _`DiscordMessages`_, _`DiscordMessage`_, and _`DiscordMessageEmbed`_
Check the source of this page to see them in action!
### Alert
This component may take a _`title`_ and a _`type`_ of _`'danger' | 'info' | 'success' | 'warning'`_.
This uses _`title="Alert" type="info"`_:
<Alert title="Alert" type="info">
Use these appropriately!
</Alert>
### Section
<Section title="Expand me!" padding defaultClosed background gutter>
Well, hello there!
Whenever some text does not need to be in the main body, you can put it here.
- _`title`_: The title that'll appear.
- _`padding`_: Adds padding.
- _`dense`_: When _`padding`_ is specified, _`dense`_ could make it appear, well, dense.
- _`defaultClosed`_ Whether the section is closed by default. This one was.
- _`background`_ Adds background to the content.
- _`gutter`_: This adds a very small appealing space between the expansion of the section and its content.
</Section>
### DiscordMessages, DiscordMessage, and DiscordMessageEmbed
<DiscordMessages>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
bot: true,
time: 'Today at 21:00',
username: 'Guide Bot',
}}
>
A _`DiscordMessage`_ must be within _`DiscordMessages`_.
</DiscordMessage>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
bot: true,
time: 'Today at 21:01',
username: 'Guide Bot',
}}
reply={{
author: {
avatar: '/assets/discordjs.png',
bot: true,
username: 'Guide Bot',
},
content: 'A _`DiscordMessage`_ must be within _`DiscordMessages`_.',
}}
time="21:02"
>
It's much better to see the source code of this page to replicate and learn!
</DiscordMessage>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
bot: true,
time: 'Today at 21:02',
username: 'Guide Bot',
}}
>
This message depicts the use of embeds.
<>
<DiscordMessageEmbed
author={{
avatar: '/assets/discordjs.png',
username: 'Guide Bot',
}}
footer={{ content: 'Sometimes, titles just have to be.' }}
title={{ title: 'An amazing title' }}
>
This is a description. You can put a description here. It must be descriptive!
</DiscordMessageEmbed>
<DiscordMessageEmbed
author={{
avatar: '/assets/discordjs.png',
username: 'Guide Bot',
}}
footer={{ content: "When one amazing title just wasn't enough." }}
title={{ title: 'Another amazing title' }}
>
Multiple embeds!
</DiscordMessageEmbed>
</>
</DiscordMessage>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
bot: true,
time: 'Today at 21:03',
username: 'Guide Bot',
}}
interaction={{
author: {
avatar: '/assets/discordjs.png',
bot: true,
username: 'Guide Bot',
},
command: '/interaction',
}}
>
Interactions are supported! I definitely used a command.
</DiscordMessage>
</DiscordMessages>
## Code blocks
We use [Code Hike](https://codehike.org). Here are some example code blocks, which should be easy to grasp and learn upon reading the source code of this page:
<CH.Code>
```ts
const HELLO = 'hello' as const;
console.log(HELLO);
// "ts" is the language of the code block.
```
</CH.Code>
<CH.Code>
```ts fileName
const FILE_NAME = 'fileName' as const;
if (FILE_NAME.includes(' ')) throw new Error('Spaces cannot be used in file names.');
```
```ts anotherFileName
const FILE_NAME_2 = 'anotherFileName' as const;
// Putting code blocks together makes them appear in tabs, just like in your editor.
```
---
```ts requiredName
const FILE_NAME_3 = 'requiredName' as const;
if (!FILE_NAME) throw new Error('There must be a file name to use panels!');
// The --- divider was used to create a panel.
```
</CH.Code>
For more information, be sure to check out the [documentation](https://codehike.org/docs/ch-code).

View File

@@ -5,17 +5,38 @@ category: Creating your bot
# Initial files
Once you [add your bot to a server](/preparations/adding-your-bot-to-servers.md), the next step is to start coding and get it online! Let's start by creating a config file for your client token and a main file for your bot application.
Once you [add your bot to a server](preparations/adding-your-bot-to-servers.md), the next step is to start coding and get it online! Let's start by initializing your package.json, creating a config file for your client token, and a main file for your bot application.
## Creating configuration files
## Creating package.json
As explained in the ["What is a token, anyway?"](/preparations/setting-up-a-bot-application.md#what-is-a-token-anyway) section, your token is essentially your bot's password, and you should protect it as best as possible. This can be done through a _`config.json`_ file or by using environment variables.
This command creates a _`package.json`_ file for you, which will keep track of the dependencies your project uses, as well as other information.
<CH.Code>
```sh npm
npm init -y; npm pkg set type="module"
```
```sh yarn
yarn add dotenv
# You must go into your package.json file and add "type": "module"
```
```sh pnpm
pnpm init; pnpm pkg set type="module"
```
</CH.Code>
Once you're done with that, onto the next step!
## Using config.json
As explained in the ["What is a token, anyway?"](preparations/setting-up-a-bot-application.md#what-is-a-token-anyway) section, your token is essentially your bot's password, and you should protect it as best as possible. This can be done through a _`config.json`_ file or by using environment variables.
Open your application in the [Discord Developer Portal](https://discord.com/developers/applications) and go to the "Bot" page to copy your token.
### Using config.json
Storing data in a _`config.json`_ file is a common way of keeping your sensitive values safe. Create a _`config.json`_ file in your project directory and paste in your token. You can access your token inside other files by using _`require()`_.
Storing data in a _`config.json`_ file is a common way of keeping your sensitive values safe. Create a _`config.json`_ file in your project directory and paste in your token.
<CH.Code>
@@ -25,12 +46,16 @@ Storing data in a _`config.json`_ file is a common way of keeping your sensitive
}
```
---
</CH.Code>
```js Usage
const { token } = require('./config.json');
You can then access your token inside other files by using _`import`_.
console.log(token);
<CH.Code>
```ts
import config from './config.json' assert { type: 'json' };
console.log(config.token);
```
</CH.Code>
@@ -68,7 +93,9 @@ console.log(process.env.DISCORD_TOKEN);
Another common approach is storing these values in a _`.env`_ file. This spares you from always copying your token into the command line. Each line in a _`.env`_ file should hold a _`KEY=value`_ pair.
You can use the [_`dotenv`_ package](https://www.npmjs.com/package/dotenv) for this. Once installed, require and use the package to load your _`.env`_ file and attach the variables to _`process.env`_:
You can use the [_`dotenv`_ package](https://www.npmjs.com/package/dotenv) for this. Once installed, preload the package to load your _`.env`_ file and attach the variables to _`process.env`_:
##### Installing dotenv
<CH.Code>
@@ -84,7 +111,11 @@ yarn add dotenv
pnpm add dotenv
```
---
</CH.Code>
##### Defining your variables
<CH.Code>
```text .env
A=123
@@ -92,18 +123,6 @@ B=456
DISCORD_TOKEN=your-token-goes-here
```
---
```js Usage
const dotenv = require('dotenv');
dotenv.config();
console.log(process.env.A);
console.log(process.env.B);
console.log(process.env.DISCORD_TOKEN);
```
</CH.Code>
<Alert title="Caution" type="danger">
@@ -111,6 +130,28 @@ console.log(process.env.DISCORD_TOKEN);
_`.gitignore`_](/creating-your-bot/#git-and-gitignore).
</Alert>
##### Utilizing your variables
<CH.Code>
```sh node
node --require dotenv/config yourFile.js
```
```sh yarn
yarn node --require dotenv/config yourFile.js
```
---
```ts yourFile
console.log(process.env.A); // 123
console.log(process.env.B); // 456
console.log(process.env.DISCORD_TOKEN); // your-token-goes-here
```
</CH.Code>
<Section title="Online editors (Glitch, Heroku, Replit, etc.)" defaultClosed padded background gutter>
While we generally do not recommend using online editors as hosting solutions, but rather invest in a proper virtual private server, these services do offer ways to keep your credentials safe as well! Please see the respective service's documentation and help articles for more information on how to keep sensitive values safe:
@@ -145,44 +186,46 @@ config.json
## Creating the main file
Open your code editor and create a new file. We suggest that you save the file as _`index.js`_, but you may name it whatever you wish.
Open your code editor and create a new file. We suggest that you save the file as _`index.ts`_, or _`index.js`_, depending on whether you use TypeScript. You may name it whatever you wish, however.
Here's the base code to get you started:
<CH.Code>
```js
// Require the necessary discord.js classes
const { Client, GatewayIntentBits } = require('discord.js');
const { token } = require('./config.json');
```ts index.ts
// Import the necessary structures.
import { Client, Events, GatewayIntentBits } from 'discord.js';
import config from './config.json';
// Create a new client instance
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
// Create a new client instance.
const client = new Client({ intents: GatewayIntentBits.Guilds });
// When the client is ready, run this code (only once)
client.once('ready', () => {
// When the client is ready, run this code (only once).
client.once(Events.ClientReady, () => {
console.log('Ready!');
});
// Login to Discord with your client's token
client.login(token);
// Log in to Discord with your client's token.
client.login(config.token);
```
</CH.Code>
This is how you create a client instance for your Discord bot and login to Discord. The _`GatewayIntentBits.Guilds`_ intents option is necessary for your client to work properly, as it ensures that the caches for guilds, channels and roles are populated and available for internal use.
Intents also define which events Discord should send to your bot, and you may wish to enable more than just the minimum. You can read more about the other intents on the [Intents topic](/popular-topics/intents).
Intents also define which events Discord should send to your bot, and you may wish to enable more than just the minimum. You can read more about the other intents on the [Intents topic](popular-topics/intents).
Open your terminal and run _`node index.js`_ to start the process. If you see "Ready!" after a few seconds, you're good to go!
Open your terminal, compile your code (JavaScript users do not do this), and run _`node index.js`_ to start the process. If you see "Ready!" after a few seconds, you're good to go!
<Alert title="Tip" type="success">
You can open your _`package.json`_ file and edit the _`"main": "index.js"`_ field to point to your main file. You can
then run _`node .`_ in your terminal to start the process! After closing the process with _`Ctrl + C`_, you can press
the up arrow on your keyboard to bring up the latest commands you've run. Pressing up and then enter after closing the
process is a quick way to start it up again.
then run _`node .`_ in your terminal to start the process! After closing the process with <kbd>⌃ Control</kbd>{' '}
<kbd>C</kbd>, you can press <kbd>↑</kbd> on your keyboard to bring up the latest commands you've run. Pressing{' '}
<kbd>↑</kbd> then <kbd>⏎ Enter</kbd> after closing the process is a quick way to start it up again.
</Alert>
## Resulting code
<ResultingCode path="creating-your-bot/initial-files" />
Code is indeed a result of code. That being said, it's being worked on. With code. Definitely.

View File

@@ -97,9 +97,9 @@ Once you fill in these values, run _`node deploy-commands.js`_ in your project d
## Replying to commands
Once you've registered your commands, you can listen for interactions via <DocsLink path="class/Client?scrollTo=e-interactionCreate" /> in your _`index.js`_ file.
Once you've registered your commands, you can listen for interactions via <DocsLink type="class" parent="Client" symbol="e-interactionCreate" /> in your _`index.js`_ file.
You should first check if an interaction is a chat input command via <DocsLink path="class/Interaction?scrollTo=isChatInputCommand" type="method">_`.isChatInputCommand()`_</DocsLink>, and then check the <DocsLink path="class/CommandInteraction?scrollTo=commandName">_`.commandName`_</DocsLink> property to know which command it is. You can respond to interactions with <DocsLink path="class/CommandInteraction?scrollTo=reply">_`.reply()`_</DocsLink>.
You should first check if an interaction is a chat input command via <DocsLink type="class" parent="BaseInteraction" symbol="isChatInputCommand" brackets>_`.isChatInputCommand()`_</DocsLink>, and then check the <DocsLink type="class" parent="CommandInteraction" symbol="commandName">_`.commandName`_</DocsLink> property to know which command it is. You can respond to interactions with <DocsLink type="class" parent="CommandInteraction" symbol="reply" brackets>_`.reply()`_</DocsLink>.
<CH.Code>
@@ -126,7 +126,7 @@ client.login(token);
### Server info command
Note that servers are referred to as "guilds" in the Discord API and discord.js library. _`interaction.guild`_ refers to the guild the interaction was sent in (a <DocsLink path="class/Guild" /> instance), which exposes properties such as _`.name`_ or _`.memberCount`_.
Note that servers are referred to as "guilds" in the Discord API and discord.js library. _`interaction.guild`_ refers to the guild the interaction was sent in (a <DocsLink type="class" parent="Guild" /> instance), which exposes properties such as _`.name`_ or _`.memberCount`_.
<CH.Code>
@@ -171,12 +171,13 @@ client.on('interactionCreate', async (interaction) => {
You could also display the date the server was created, or the server's verification level. You would do those in the same manner use _`interaction.guild.createdAt`_ or _`interaction.guild.verificationLevel`_, respectively.
<Alert title="Tip" type="success">
Refer to the <DocsLink path="class/Guild" /> documentation for a list of all the available properties and methods!
Refer to the <DocsLink type="class" parent="Guild" /> documentation for a list of all the available properties and
methods!
</Alert>
### User info command
A "user" refers to a Discord user. _`interaction.user`_ refers to the user the interaction was sent by (a <DocsLink path="class/User" /> instance), which exposes properties such as _`.tag`_ or _`.id`_.
A "user" refers to a Discord user. _`interaction.user`_ refers to the user the interaction was sent by (a <DocsLink type="class" parent="User" /> instance), which exposes properties such as _`.tag`_ or _`.id`_.
<CH.Code>
@@ -219,7 +220,8 @@ client.on('interactionCreate', async (interaction) => {
</DiscordMessages>
<Alert title="Tip" type="success">
Refer to the <DocsLink path="class/User" /> documentation for a list of all the available properties and methods!
Refer to the <DocsLink type="class" parent="User" /> documentation for a list of all the available properties and
methods!
</Alert>
And there you have it!

View File

@@ -102,15 +102,15 @@ Now that you know how Promises work and what they are used for, let's look at an
<CH.Code>
```js
const { Client, GatewayIntentBits } = require('discord.js');
import { Client, Events, GatewayIntentBits } from 'discord.js';
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once('ready', () => {
client.once(Events.ClientReady, () => {
console.log('I am ready!');
});
client.on('interactionCreate', (interaction) => {
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'react') {

View File

@@ -106,7 +106,7 @@ collection.sweep((user) => user.username === 'Bob');
</CH.Code>
A more complicated method is _`partition`_, which splits a single Collection into two new Collections based on the provided function.
You can think of it as two \_`filter`\_s, but done at the same time:
You can think of it as two \_`filter`\_ methods, but done at the same time:
<CH.Code>

View File

@@ -0,0 +1,805 @@
---
title: Updating to v14
category: Additional info
---
# Updating to v14
## Before you start
v14 requires Node 16.9 or higher to use, so make sure you're up to date. To check your Node.js version, use _`node --version`_ in your terminal or command prompt, and if it's not high enough, update it! There are many resources online to help you with this step based on your host system.
### Various packages are now included in v14
If you previously had _`@discordjs/builders`_, _`@discordjs/formatters`_, _`@discordjs/rest`_, or _`discord-api-types`_ manually installed, it's _highly_ recommended that you uninstall the packages to avoid package version conflicts.
<CH.Code>
```sh npm
npm uninstall @discordjs/builders @discordjs/formatters @discordjs/rest discord-api-types
```
```sh yarn
yarn remove @discordjs/builders @discordjs/formatters @discordjs/rest discord-api-types
```
```sh pnpm
pnpm remove @discordjs/builders @discordjs/formatters @discordjs/rest discord-api-types
```
</CH.Code>
## Breaking Changes
### API version
discord.js v14 makes the switch to Discord API v10!
### Common Breakages
### Enum Values
Any areas that used to accept a _`string`_ or _`number`_ type for an enum parameter will now only accept a _`number`_.
In addition, the old enums exported by discord.js v13 and lower are replaced with new enums from [discord-api-types](https://discord-api-types.dev/api/discord-api-types-v10).
#### New enum differences
Most of the difference between enums from discord.js and discord-api-types can be summarized as so:
1. Enums are singular, i.e., _`ApplicationCommandOptionTypes`_ -> _`ApplicationCommandOptionType`_
2. Enums that are prefixed with _`Message`_ no longer have the _`Message`_ prefix, i.e., _`MessageButtonStyles`_ -> _`ButtonStyle`_
3. Enum values are _`PascalCase`_ rather than `SCREAMING_SNAKE_CASE`, i.e., `.CHAT_INPUT` -> `.ChatInput`
<Alert title="Magic Numbers Warning" type="danger">
You might be inclined to a raw _`number`_ (most commonly referred to as [magic
numbers](https://en.wikipedia.org/wiki/Magic_number_(programming))) instead of enum values. This is highly
discouraged. Enums provide more readability and are more resistant to changes in the API. Magic numbers can obscure
the meaning of your code in many ways. Check out this [blog
post](https://blog.webdevsimplified.com/2020-02/magic-numbers) if you want more context on as to why they shouldn't be
used.
</Alert>
#### Common enum breakages
Areas like _`Client`_ initialization, JSON slash commands and JSON message components will likely need to be modified to accommodate these changes:
##### Common Client Initialization Changes
<CH.Code>
```diff
- import { Client, Intents } = from 'discord.js';
+ import { Client, GatewayIntentBits, Partials } = from 'discord.js';
- const client = new Client({ intents: [Intents.FLAGS.GUILDS], partials: ['CHANNEL'] });
+ const client = new Client({ intents: [GatewayIntentBits.Guilds], partials: [Partials.Channel] });
```
</CH.Code>
##### Common Application Command Data changes
<CH.Code>
```diff
+ import { ApplicationCommandType, ApplicationCommandOptionType } = from 'discord.js';
const command = {
name: 'ping',
- type: 'CHAT_INPUT',
+ type: ApplicationCommandType.ChatInput,
options: [{
name: 'option',
description: 'A sample option',
- type: 'STRING',
+ type: ApplicationCommandOptionType.String,
}],
};
```
</CH.Code>
##### Common Button Data changes
<CH.Code>
```diff
+ import { ButtonStyle } = from 'discord.js';
const button = {
label: 'test',
- style: 'PRIMARY',
+ style: ButtonStyle.Primary,
customId: '1234'
}
```
</CH.Code>
### Removal of method-based type guards
#### Channels
Some channel type guard methods that narrowed to one channel type have been removed. Instead compare the _`type`_ property against a [ChannelType](https://discord-api-types.dev/api/discord-api-types-v10/enum/ChannelType) enum member to narrow channels.
<CH.Code>
```diff
- channel.isText();
+ channel.type === ChannelType.GuildText;
- channel.isVoice();
+ channel.type === ChannelType.GuildVoice;
- channel.isDM();
+ channel.type === ChannelType.DM;
```
</CH.Code>
### Builders
Builders are no longer returned by the API like they were previously. For example, you send the API an <DocsLink type="class" parent="EmbedBuilder"/> but you receive an <DocsLink type="class" parent="Embed"/> of the same data. This may affect how your code handles received structures such as components. Refer to [message component changes section](#messagecomponent) for more details.
Added <DocsLink package="builders" type="Function" parent="disableValidators" /> and <DocsLink package="builders" type="Function" parent="enableValidators" /> as top-level exports which disable or enable validation (enabled by default).
### Consolidation of create & edit parameters
Various _`create()`_ and _`edit()`_ methods on managers and objects have had their parameters consolidated. The changes are below:
- <DocsLink type="class" parent="Guild" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="GuildChannel" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="GuildEmoji" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="Role" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="Sticker" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="ThreadChannel" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="create" brackets /> now takes _`name`_ in the _`options`_ parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="createWebhook" brackets /> (and other text-based channels)
now takes _`channel`_ and _`name`_ in the _`options`_ parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildEmojiManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildManager" symbol="create" brackets /> now takes _`name`_ as a part of _`options`_
- <DocsLink type="class" parent="GuildMemberManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildMember" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildStickerManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="RoleManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`options`_
- <DocsLink type="class" parent="Webhook" symbol="edit" brackets /> now takes _`reason`_ as a part of _`options`_
- <DocsLink type="class" parent="GuildEmojiManager" symbol="create" brackets /> now takes _`attachment`_ and _`name`_ as
a part of _`options`_
- <DocsLink type="class" parent="GuildStickerManager" symbol="create" brackets /> now takes _`file`_, _`name`_, and _`tags`_
as a part of _`options`_
### Activity
The following properties have been removed as they are not supported by the API:
- _`Activity#id`_
- _`Activity#platform`_
- _`Activity#sessionId`_
- _`Activity#syncId`_
### Application
_`Application#fetchAssets()`_ has been removed as it is no longer supported by the API.
### BitField
- BitField constituents now have a _`BitField`_ suffix to avoid naming conflicts with the enum names:
```diff
- new Permissions();
+ new PermissionsBitField();
- new MessageFlags();
+ new MessageFlagsBitField();
- new ThreadMemberFlags();
+ new ThreadMemberFlagsBitField();
- new UserFlags();
+ new UserFlagsBitField();
- new SystemChannelFlags();
+ new SystemChannelFlagsBitField();
- new ApplicationFlags();
+ new ApplicationFlagsBitField();
- new Intents();
+ new IntentsBitField();
- new ActivityFlags();
+ new ActivityFlagsBitField();
```
- _`#FLAGS`_ has been renamed to _`#Flags`_
### CDN
The methods that return CDN URLs have changed. Here is an example on a `User`:
<CH.Code>
```diff
- const url = user.displayAvatarURL({ dynamic: true, format: "png", size: 1024 });
+ const url = user.displayAvatarURL({ extension: "png", size: 1024 });
```
</CH.Code>
Dynamic URLs use <DocsLink package="rest" type="Interface" parent="ImageURLOptions"/> and static URLs use <DocsLink package="rest" type="Interface" parent="BaseImageURLOptions"/>. Since dynamic URLs are returned by default, this option has been renamed to _`forceStatic`_ which forces the return of a static URL. Additionally, _`format`_ has been renamed to _`extension`_.
### CategoryChannel
<DocsLink type="class" parent="CategoryChannel" symbol="children" /> is no longer a _`Collection`_ of channels the category
contains. It is now a <DocsLink type="class" parent="CategoryChannelChildManager" />. This also means
_`CategoryChannel#createChannel()`_ has been moved to the <DocsLink type="class" parent="CategoryChannelChildManager" />.
### Channel
The following type guards have been removed:
- _`Channel#isText()`_
- _`Channel#isVoice()`_
- _`Channel#isDirectory()`_
- _`Channel#isDM()`_
- _`Channel#isGroupDM()`_
- _`Channel#isCategory()`_
- _`Channel#isNews()`_
Refer to [this section](#channels) for more context.
The base channel class is now <DocsLink type="class" parent="BaseChannel"/>.
### Client
The _`restWsBridgeTimeout`_ client option has been removed.
### CommandInteractionOptionResolver
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getMember" brackets /> no longer has a parameter
for _`required`_.[^1]
### Constants
- Many constant objects and key arrays are now top-level exports. For example:
<CH.Code>
```diff
- import { Constants } = from 'discord.js';
- const { Colors } = Constants;
+ import { Colors } = from 'discord.js';
```
</CH.Code>
- The refactored constants structures have _`PascalCase`_ member names as opposed to _`SCREAMING_SNAKE_CASE`_ member names.
- Many of the exported constants structures have been replaced and renamed:
<CH.Code>
```diff
- Opcodes
+ GatewayOpcodes
- WSEvents
+ GatewayDispatchEvents
- WSCodes
+ GatewayCloseCodes
- InviteScopes
+ OAuth2Scopes
```
</CH.Code>
### Events
The _`message`_ and _`interaction`_ events are now removed. Use <DocsLink type="class" parent="Client" symbol="e-messageCreate"/> and <DocsLink type="class" parent="Client" symbol="e-interactionCreate"/> instead.
_`Client#applicationCommandCreate`_, _`Client#applicationCommandDelete`_, and _`Client#applicationCommandUpdate`_ have all been removed.[^2]
The <DocsLink type="class" parent="Client" symbol="e-threadMembersUpdate"/> event now emits the users that were added, the users that were removed, and the thread respectively.
### GuildBanManager
Developers should utilise _`deleteMessageSeconds`_ instead of _`days`_ and _`deleteMessageDays`_:
<CH.Code>
```diff
<GuildBanManager>.create('123456789', {
- days: 3
- deleteMessageDays: 3
+ deleteMessageSeconds: 3 * 24 * 60 * 60
});
```
</CH.Code>
_`deleteMessageDays`_ and _`days`_ are both deprecated and will be removed in the future.
### Guild
<DocsLink type="class" parent="Guild" symbol="setRolePositions" brackets /> and <DocsLink
type="class"
parent="Guild"
symbol="setChannelPositions"
brackets
/> have been removed. Use <DocsLink type="class" parent="RoleManager" symbol="setPositions" brackets /> and <DocsLink
type="class"
parent="GuildChannelManager"
symbol="setPositions"
brackets
/> instead respectively.
<DocsLink type="class" parent="Guild" symbol="maximumPresences" /> no longer has a default value of 25,000.
_`Guild#me`_ has been moved to <DocsLink type="class" parent="GuildMemberManager" symbol="me" />.[^3]
### GuildAuditLogs & GuildAuditLogsEntry
_`GuildAuditLogs.build()`_ has been removed as it has been deemed defunct. There is no alternative.
The following properties & methods have been moved to the <DocsLink type="class" parent="GuildAuditLogsEntry" /> class:
- `GuildAuditLogs.Targets`
- `GuildAuditLogs.actionType()`
- `GuildAuditLogs.targetType()`
### GuildMember
<DocsLink type="class" parent="GuildMember" symbol="pending" /> is now nullable to account for partial guild members.[^4]
### IntegrationApplication
_`IntegrationApplication#summary`_ has been removed as it is no longer supported by the API.
### Interaction
Whenever an interaction is replied to and one fetches the reply, it could possibly give an [APIMessage](https://discord-api-types.dev/api/discord-api-types-v10/interface/APIMessage) if the guild was not cached. However, interaction replies now always return a discord.js <DocsLink type="class" parent="Message"/> object with _`fetchReply`_ as _`true`_.
The base interaction class is now <DocsLink type="class" parent="BaseInteraction"/>.
### Invite
<DocsLink type="class" parent="Invite" symbol="inviter" /> is now a getter and resolves structures from the cache.
### MessageAttachment
- _`MessageAttachment`_ has now been renamed to <DocsLink type="class" parent="AttachmentBuilder" />.
<CH.Code>
```diff
- new MessageAttachment(buffer, 'image.png');
+ new AttachmentBuilder(buffer, { name: 'image.png' });
```
</CH.Code>
### MessageComponent
- MessageComponents have been renamed as well. They no longer have the _`Message`_ prefix, and now have a _`Builder`_ suffix:
<CH.Code>
```diff
- const button = new MessageButton();
+ const button = new ButtonBuilder();
- const selectMenu = new MessageSelectMenu();
+ const selectMenu = new StringSelectMenuBuilder();
- const actionRow = new MessageActionRow();
+ const actionRow = new ActionRowBuilder();
- const textInput = new TextInputComponent();
+ const textInput = new TextInputBuilder();
```
</CH.Code>
- Components received from the API are no longer directly mutable. If you wish to mutate a component from the API, use _`ComponentBuilder#from()`_. For example, if you want to make a button mutable:
<CH.Code>
```diff
- const editedButton = receivedButton.setDisabled(true);
+ import { ButtonBuilder } = from 'discord.js';
+ const editedButton = ButtonBuilder.from(receivedButton).setDisabled(true);
```
</CH.Code>
### MessageManager
The second paramter of <DocsLink type="class" parent="MessageManager" symbol="fetch" brackets /> has been removed. The <DocsLink type="class" parent="BaseFetchOptions" /> the second parameter once was is now merged into the first parameter.
<CH.Code>
```diff
- messageManager.fetch('1234567890', { cache: false, force: true });
+ messageManager.fetch({ message: '1234567890', cache: false, force: true });
```
</CH.Code>
### MessageSelectMenu
- _`MessageSelectMenu`_ has been renamed to <DocsLink type="class" parent="StringSelectMenuBuilder" />.
- _`StringSelectMenuBuilder#addOption()`_ has been removed. Use <DocsLink type="class" parent="StringSelectMenuBuilder" symbol="addOptions" brackets /> instead.
### MessageEmbed
- _`MessageEmbed`_ has now been renamed to <DocsLink type="class" parent="EmbedBuilder" />.
- <DocsLink package="builders" type="Class" parent="EmbedBuilder" symbol="setAuthor" brackets /> now accepts a sole <DocsLink
package="builders"
type="TypeAlias"
parent="EmbedAuthorOptions"
/> object.
- <DocsLink package="builders" type="Class" parent="EmbedBuilder" symbol="setFooter" brackets /> now accepts a sole <DocsLink
package="builders"
type="TypeAlias"
parent="EmbedFooterOptions"
/> object.
- _`EmbedBuilder#addField()`_ has been removed. Use <DocsLink package="builders" type="Class" parent="EmbedBuilder" symbol="addFields" brackets/> instead.
<CH.Code>
```diff
- new MessageEmbed().addField('Inline field title', 'Some value here', true);
+ new EmbedBuilder().addFields([
+ { name: 'one', value: 'one', inline: true },
+ { name: 'two', value: 'two', inline: true },
+]);
```
</CH.Code>
### Modal
- _`Modal`_ has been renamed <DocsLink type="class" parent="ModalBuilder" />.
<CH.Code>
```diff
- const modal = new Modal();
+ const modal = new ModalBuilder();
```
</CH.Code>
### PartialTypes
The _`PartialTypes`_ string array has been removed. Use the <DocsLink type="typedef" parent="Partials" /> enum instead.
In addition to this, there is now a new partial: _`Partials.ThreadMember`_.
### Permissions
The thread permissions _`USE_PUBLIC_THREADS`_ and _`USE_PRIVATE_THREADS`_ have been removed as they are deprecated in the API. Use _`CREATE_PUBLIC_THREADS`_ and _`CREATE_PRIVATE_THREADS`_ respectively.
### PermissionOverwritesManager
Overwrites are now keyed by the _`PascalCase`_ permission key rather than the _`SCREAMING_SNAKE_CASE`_ permission key.
### REST Events
#### apiRequest
This REST event has been removed as discord.js now uses [Undici](https://github.com/nodejs/undici) as the underlying request handler. You must now use a [Diagnostics Channel](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel). Here is a simple example:
<CH.Code>
```js JavaScript
import diagnosticsChannel from 'node:diagnostics_channel';
diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {
const { request } = data;
console.log(request.method); // Log the method
console.log(request.path); // Log the path
console.log(request.headers); // Log the headers
console.log(request); // Or just log everything!
});
```
```ts TypeScript
import diagnosticsChannel from 'node:diagnostics_channel';
import { type DiagnosticsChannel } from 'undici';
diagnosticsChannel.channel('undici:request:create').subscribe((data) => {
const { request } = data as DiagnosticsChannel.RequestCreateMessage;
console.log(request.method); // Log the method
console.log(request.path); // Log the path
console.log(request.headers); // Log the headers
console.log(request); // Or just log everything!
});
```
</CH.Code>
You can find further examples at the [Undici Diagnostics Channel documentation](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel).
#### apiResponse
This REST event has been renamed to _`response`_ and moved to <DocsLink type="class" parent="Client" symbol="rest"/>:
<CH.Code>
```diff
- client.on('apiResponse', ...);
+ client.rest.on('response', ...);
```
</CH.Code>
#### invalidRequestWarning
This REST event has been moved to <DocsLink type="class" parent="Client" symbol="rest"/>:
<CH.Code>
```diff
- client.on('invalidRequestWarning', ...);
+ client.rest.on('invalidRequestWarning', ...);
```
</CH.Code>
#### rateLimit
This REST event has been renamed to _`rateLimited`_ and moved to <DocsLink type="class" parent="Client" symbol="rest"/>:
<CH.Code>
```diff
- client.on('rateLimit', ...);
+ client.rest.on('rateLimited', ...);
```
</CH.Code>
### RoleManager
_`Role.comparePositions()`_ has been removed. Use <DocsLink type="class" parent="RoleManager" symbol="comparePositions" brackets/> instead.
### Sticker
<DocsLink type="class" parent="Sticker" symbol="tags" /> is now a nullable string (_`string | null`_). Previously, it was
a nullable array of strings (_`string[] | null`_).[^5]
### ThreadChannel
The _`MAX`_ helper used in _`ThreadAutoArchiveDuration`_ has been removed. Discord has since allowed any guild to use any auto archive time which makes this helper redundant.
### ThreadMemberManager
The second parameter of <DocsLink type="class" parent="ThreadMemberManager" symbol="fetch" brackets /> has been removed. The <DocsLink type="class" parent="BaseFetchOptions" /> the second parameter once was is now merged into the first parameter. In addition, the boolean helper to specify _`cache`_ has been removed.
Usage is now as follows:
<CH.Code>
```diff
// The second parameter is merged into the first parameter.
- threadMemberManager.fetch('1234567890', { cache: false, force: true });
+ threadMemberManager.fetch({ member: '1234567890', cache: false, force: true });
// The lone boolean has been removed. One must be explicit here.
- threadMemberManager.fetch(false);
+ threadMemberManager.fetch({ cache: false });
```
</CH.Code>
### Util
_`Util.removeMentions()`_ has been removed. To control mentions, you should use _`allowedMentions`_ on <DocsLink type="typedef" parent="BaseMessageOptions" /> instead.
_`Util.splitMessage()`_ has been removed. This utility method is something the developer themselves should do.
_`Util.resolveAutoArchiveMaxLimit()`_ has been removed. Discord has since allowed any guild to use any auto archive time which makes this method redundant.
Other functions in _`Util`_ have been moved to top-level exports so you can directly import them from discord.js.
<CH.Code>
```diff
- import { Util } from 'discord.js';
- Util.escapeMarkdown(message);
+ import { escapeMarkdown } from 'discord.js';
+ escapeMarkdown(message);
```
</CH.Code>
### .deleted fields have been removed
You can no longer use the _`deleted`_ property to check if a structure was deleted.[^6]
### VoiceChannel
_`VoiceChannel#editable`_ has been removed. You should use <DocsLink type="class" parent="GuildChannel" symbol="manageable"/> instead.
### VoiceRegion
_`VoiceRegion#vip`_ has been removed as it is no longer part of the API.
### Webhook
The second parameter of <DocsLink type="class" parent="Webhook" symbol="fetchMessage" brackets/> no longer allows a boolean to be passed. The _`cache`_ option in <DocsLink type="typedef" parent="WebhookFetchMessageOptions"/> should be used instead.
## Features
### ApplicationCommand
NFSW commands are supported.
### AutocompleteInteraction
<DocsLink type="class" parent="AutocompleteInteraction" symbol="commandGuildId" /> has been added which is the id of the
guild the invoked application command is registered to.
### BaseChannel
Added support for <DocsLink type="class" parent="BaseChannel" symbol="flags" />.
Store channels have been removed as they are no longer part of the API.
<DocsLink type="class" parent="BaseChannel" symbol="url" /> has been added which is a link to a channel, just like in the
client.
Additionally, new typeguards have been added:
- <DocsLink type="class" parent="BaseChannel" symbol="isDMBased" brackets />
- <DocsLink type="class" parent="BaseChannel" symbol="isTextBased" brackets />
- <DocsLink type="class" parent="BaseChannel" symbol="isVoiceBased" brackets />
### BaseInteraction
Added <DocsLink type="class" parent="BaseInteraction" symbol="isRepliable" brackets /> to check whether a given interaction can be replied to.
### ClientApplication
Added support for role connection metadata.
### Collection
- Added <DocsLink package="collection" type="Class" parent="Collection" symbol="merge" brackets/> and <DocsLink package="collection" type="Class" parent="Collection" symbol="combineEntries" brackets/>.
- Added <DocsLink package="collection" type="TypeAlias" parent="ReadonlyCollection"/> which indicates an immutable _`Collection`_.
### Collector
A new <DocsLink type="class" parent="Collector" symbol="e-ignore"/> event has been added which is emitted whenever an element is not collected by the collector.
Component collector options now use the [ComponentType](https://discord-api-types.dev/api/discord-api-types-v10/enum/ComponentType) enum values:
<CH.Code>
```diff
+ import { ComponentType } from 'discord.js';
const collector = interaction.channel.createMessageComponentCollector({
filter,
- componentType: 'BUTTON',
+ componentType: ComponentType.Button,
time: 20000
});
```
</CH.Code>
### CommandInteraction
<DocsLink type="class" parent="CommandInteraction" symbol="commandGuildId" /> has been added which is the id of the guild
the invoked application command is registered to.
### CommandInteractionOptionResolver
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getChannel" brackets /> now has a third parameter
which narrows the channel type.
### Events
Added support for <DocsLink type="class" parent="Client" symbol="e-guildAuditLogEntryCreate" /> event.
### ForumChannel
Added support for forum channels.
Added support for <DocsLink type="class" parent="ForumChannel" symbol="defaultForumLayout" />.
### Guild
Added support for <DocsLink type="class" parent="Guild" symbol="setMFALevel" brackets /> which sets the guild's MFA level.
Added support for <DocsLink type="class" parent="Guild" symbol="maxVideoChannelUsers"/>. which indicates the maximum number of video channel users.
Added support for <DocsLink type="class" parent="Guild" symbol="maxStageVideoChannelUsers" />. which indicates the maximum number of video channel users for stage channels.
Added support for <DocsLink type="class" parent="Guild" symbol="disableInvites" brackets />. which disables the guild's invites.
Added support for the _`after`_ parameter in <DocsLink type="class" parent="Guild" symbol="fetchAuditLogs" brackets />.
### GuildChannelManager
_`videoQualityMode`_ may be used whilst creating a channel to initially set the camera video quality mode.
### GuildEmojiManager
Added <DocsLink type="class" parent="GuildEmojiManager" symbol="delete" brackets /> and <DocsLink type="class" parent="GuildEmojiManager" symbol="edit" brackets /> for managing existing guild emojis.
### GuildForumThreadManager
Added <DocsLink type="class" parent="GuildForumThreadManager" /> as manager for threads in forum channels.
### GuildMember
Added support for <DocsLink type="class" parent="GuildMember" symbol="flags"/>.
### GuildMembersChunk
This object now supports the _`notFound`_ property.
### GuildMemberManager
Added <DocsLink type="class" parent="GuildMemberManager" symbol="fetchMe" brackets /> to fetch the client user in the guild.
Added <DocsLink type="class" parent="GuildMemberManager" symbol="addRole" brackets /> and <DocsLink type="class" parent="GuildMemberManager" symbol="removeRole" brackets />. These methods allow a single addition or removal of a role respectively to a guild member, even if uncached.
### GuildTextThreadManager
Added <DocsLink type="class" parent="GuildTextThreadManager" /> as manager for threads in text channels and announcement channels.
### Message
<DocsLink type="class" parent="Message" symbol="position" /> has been added as an approximate position in a thread.
Added support for role subscription data.
### MessageReaction
Added <DocsLink type="class" parent="MessageReaction" symbol="react" brackets /> to make the client user react with the reaction the class belongs to.
### Role
Added support for role subscriptions.
### StageChannel
Stage channels now allow messages to be sent in them, much like voice channels.
### Sticker
Added support for GIF stickers.
### ThreadMemberManager
The new _`withMember`_ options returns the associated guild member with the thread member.
When fetching multiple thread members alongside _`withMember`_, paginated results will be returned. The _`after`_ and _`limit`_ option are supported in this scenario.
### Webhook
Added <DocsLink type="class" parent="Webhook" symbol="applicationId" />.
Added the _`threadName`_ property in <DocsLink type="typedef" parent="WebhookMessageCreateOptions"/> which allows a webhook to create a post in a forum channel.
[^1]: https://github.com/discordjs/discord.js/pull/7188
[^2]: https://github.com/discordjs/discord.js/pull/6492
[^3]: https://github.com/discordjs/discord.js/pull/7669
[^4]: https://github.com/discordjs/discord.js/issues/6546
[^5]: https://github.com/discordjs/discord.js/pull/8010
[^6]: https://github.com/discordjs/discord.js/issues/7091

View File

@@ -1,17 +0,0 @@
---
title: Requesting more content
category: Home
---
# Requesting more content
Since this guide is made specifically for the discord.js community, we want to be sure to provide the most relevant and up-to-date content. We will, of course, make additions to the current pages and add new ones as we see fit, but fulfilling requests is how we know we're providing content you all want the most.
Requests may be as simple as "add an example to the [frequently asked questions](/popular-topics/faq.html) page", or as elaborate as "add a page regarding [sharding](/sharding/)". We'll do our best to fulfill all requests, as long as they're reasonable.
To make a request, simply head over to [the repo's issue tracker](https://github.com/discordjs/guide/issues) and [create a new issue](https://github.com/discordjs/guide/issues/new)! Title it appropriately, and let us know exactly what you mean inside the issue description. Make sure that you've looked around the site before making a request; what you want to request might already exist!
<Alert title="Tip" type="success">
Remember that you can always [fork the repo](https://github.com/discordjs/guide) and [make a pull
request](https://github.com/discordjs/guide/pulls) if you want to add anything to the guide yourself!
</Alert>

View File

@@ -1,100 +0,0 @@
---
title: Test
category: Test
---
<DiscordMessages>
<DiscordMessage
reply={{
author: {
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
},
content: 'Test',
}}
author={{
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
time: 'Today at 21:00',
}}
>
1234
</DiscordMessage>
<DiscordMessage
author={{
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
time: 'Today at 21:00',
}}
followUp
time="21:02"
>
1234
</DiscordMessage>
</DiscordMessages>
<DiscordMessages>
<DiscordMessage
reply={{
author: {
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
},
content: 'Test',
}}
author={{
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
time: 'Today at 21:00',
}}
>
1234
</DiscordMessage>
</DiscordMessages>
<DiscordMessages>
<DiscordMessage
reply={{
author: {
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
},
content: 'Test',
}}
author={{
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
time: 'Today at 21:00',
}}
>
<>
<DiscordMessageEmbed
author={{
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
}}
/>
<DiscordMessageEmbed title={{ title: 'Title' }} />
<DiscordMessageEmbed footer={{ content: 'Footer' }} />
<DiscordMessageEmbed
author={{
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
}}
title={{ title: 'Title' }}
footer={{ content: 'Footer' }}
>
Test
</DiscordMessageEmbed>
</>
</DiscordMessage>
</DiscordMessages>

View File

@@ -0,0 +1,27 @@
'use client';
import {
type PropsWithChildren,
type Dispatch,
type SetStateAction,
createContext,
useContext,
useState,
useMemo,
} from 'react';
export const NavContext = createContext<{ opened: boolean; setOpened: Dispatch<SetStateAction<boolean>> }>({
opened: false,
setOpened: (_) => {},
});
export const NavProvider = ({ children }: PropsWithChildren) => {
const [opened, setOpened] = useState(false);
const value = useMemo(() => ({ opened, setOpened }), [opened]);
return <NavContext.Provider value={value}>{children}</NavContext.Provider>;
};
export function useNav() {
return useContext(NavContext);
}

View File

@@ -0,0 +1,9 @@
import { NextResponse, type NextRequest } from 'next/server';
export default async function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/guide/home/introduction', request.url));
}
export const config = {
matcher: ['/', '/guide'],
};

View File

@@ -1,82 +0,0 @@
:root {
--shiki-color-text: #24292eff;
--shiki-color-background: #ffffff;
--shiki-token-constant: #1976d2;
--shiki-token-string: #6f42c1;
--shiki-token-comment: #c2c3c5;
--shiki-token-keyword: #d32f2f;
--shiki-token-parameter: #ff9800;
--shiki-token-function: #6f42c1;
--shiki-token-string-expression: #22863a;
--shiki-token-punctuation: #212121;
--shiki-token-link: #22863a;
--ch-tabs-bg: #f6f6f6;
--ch-tab-border: #f6f6f6;
--ch-tab-active-border: #fff;
--ch-tab-active-bg: #24292eff;
--ch-tab-inactive-color: #bdbdbd;
--ch-tab-inactive-bg: #f6f6f6;
--ch-icon-text: #24292eff;
}
.ch-code,
.ch-inline-code > code {
background: var(--shiki-color-background) !important;
}
.ch-code {
color-scheme: light !important;
}
.ch-code-multiline-mark {
background: rgba(253, 255, 0, 0.2) !important;
}
.ch-codegroup .ch-editor-button,
.ch-codeblock .ch-code-button {
color: var(--ch-icon-text) !important;
}
div.ch-editor-tab-active {
color: var(--ch-tab-active-color) !important;
}
html.dark {
--shiki-color-text: #adbac7;
--shiki-color-background: #22272e;
--shiki-token-constant: #f47067;
--shiki-token-string: #96d0ff;
--shiki-token-comment: #768390;
--shiki-token-keyword: #f47067;
--shiki-token-parameter: #adbac7;
--shiki-token-function: #dcbdfb;
--shiki-token-string-expression: #8ddb8c;
--shiki-token-punctuation: #adbac7;
--shiki-token-link: #adbac7;
--ch-tabs-bg: #1c2128;
--ch-tab-border: #444c56;
--ch-tab-active-border: #22272e;
--ch-tab-active-bg: #22272e;
--ch-tab-inactive-color: #768390;
--ch-tab-inactive-bg: #1c2128;
--ch-icon-text: #768390;
}
.dark .ch-codegroup .ch-editor-button,
.ch-codeblock .ch-code-button {
color: var(--ch-icon-text) !important;
}
.dark div.ch-editor-tab-active {
color: var(--ch-tab-active-color) !important;
}
.dark .ch-code {
color-scheme: dark !important;
}
.dark .ch-code-multiline-mark {
background: rgba(255, 255, 255, 0.043) !important;
}

View File

@@ -0,0 +1,566 @@
{
"$schema": "vscode://schemas/color-theme",
"name": "dark-plus",
"tokenColors": [
{
"settings": {
"foreground": "#D4D4D4"
}
},
{
"scope": ["meta.embedded", "source.groovy.embedded", "string meta.image.inline.markdown"],
"settings": {
"foreground": "#D4D4D4"
}
},
{
"scope": "emphasis",
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "strong",
"settings": {
"fontStyle": "bold"
}
},
{
"scope": "header",
"settings": {
"foreground": "#000080"
}
},
{
"scope": "comment",
"settings": {
"foreground": "#6A9955"
}
},
{
"scope": "constant.language",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": [
"constant.numeric",
"variable.other.enummember",
"keyword.operator.plus.exponent",
"keyword.operator.minus.exponent"
],
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "constant.regexp",
"settings": {
"foreground": "#646695"
}
},
{
"scope": "entity.name.tag",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "entity.name.tag.css",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "entity.other.attribute-name",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": [
"entity.other.attribute-name.class.css",
"entity.other.attribute-name.class.mixin.css",
"entity.other.attribute-name.id.css",
"entity.other.attribute-name.parent-selector.css",
"entity.other.attribute-name.pseudo-class.css",
"entity.other.attribute-name.pseudo-element.css",
"source.css.less entity.other.attribute-name.id",
"entity.other.attribute-name.scss"
],
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "invalid",
"settings": {
"foreground": "#f44747"
}
},
{
"scope": "markup.underline",
"settings": {
"fontStyle": "underline"
}
},
{
"scope": "markup.bold",
"settings": {
"fontStyle": "bold",
"foreground": "#569cd6"
}
},
{
"scope": "markup.heading",
"settings": {
"fontStyle": "bold",
"foreground": "#569cd6"
}
},
{
"scope": "markup.italic",
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "markup.strikethrough",
"settings": {
"fontStyle": "strikethrough"
}
},
{
"scope": "markup.inserted",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "markup.deleted",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "markup.changed",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "punctuation.definition.quote.begin.markdown",
"settings": {
"foreground": "#6A9955"
}
},
{
"scope": "punctuation.definition.list.begin.markdown",
"settings": {
"foreground": "#6796e6"
}
},
{
"scope": "markup.inline.raw",
"settings": {
"foreground": "#ce9178"
}
},
{
"name": "brackets of XML/HTML tags",
"scope": "punctuation.definition.tag",
"settings": {
"foreground": "#808080"
}
},
{
"scope": ["meta.preprocessor", "entity.name.function.preprocessor"],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "meta.preprocessor.string",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "meta.preprocessor.numeric",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "meta.structure.dictionary.key.python",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "meta.diff.header",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "storage",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "storage.type",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": ["storage.modifier", "keyword.operator.noexcept"],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": ["string", "meta.embedded.assembly"],
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.tag",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.value",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.regexp",
"settings": {
"foreground": "#d16969"
}
},
{
"name": "String interpolation",
"scope": [
"punctuation.definition.template-expression.begin",
"punctuation.definition.template-expression.end",
"punctuation.section.embedded"
],
"settings": {
"foreground": "#569cd6"
}
},
{
"name": "Reset JavaScript string interpolation expression",
"scope": ["meta.template.expression"],
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": [
"support.type.vendored.property-name",
"support.type.property-name",
"variable.css",
"variable.scss",
"variable.other.less",
"source.coffee.embedded"
],
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "keyword",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.control",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.operator",
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": [
"keyword.operator.new",
"keyword.operator.expression",
"keyword.operator.cast",
"keyword.operator.sizeof",
"keyword.operator.alignof",
"keyword.operator.typeid",
"keyword.operator.alignas",
"keyword.operator.instanceof",
"keyword.operator.logical.python",
"keyword.operator.wordlike"
],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.other.unit",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "support.function.git-rebase",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "constant.sha.git-rebase",
"settings": {
"foreground": "#b5cea8"
}
},
{
"name": "coloring of the Java import and package identifiers",
"scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"],
"settings": {
"foreground": "#d4d4d4"
}
},
{
"name": "this.self",
"scope": "variable.language",
"settings": {
"foreground": "#569cd6"
}
},
{
"name": "Function declarations",
"scope": [
"entity.name.function",
"support.function",
"support.constant.handlebars",
"source.powershell variable.other.member",
"entity.name.operator.custom-literal"
],
"settings": {
"foreground": "#DCDCAA"
}
},
{
"name": "Types declaration and references",
"scope": [
"support.class",
"support.type",
"entity.name.type",
"entity.name.namespace",
"entity.other.attribute",
"entity.name.scope-resolution",
"entity.name.class",
"storage.type.numeric.go",
"storage.type.byte.go",
"storage.type.boolean.go",
"storage.type.string.go",
"storage.type.uintptr.go",
"storage.type.error.go",
"storage.type.rune.go",
"storage.type.cs",
"storage.type.generic.cs",
"storage.type.modifier.cs",
"storage.type.variable.cs",
"storage.type.annotation.java",
"storage.type.generic.java",
"storage.type.java",
"storage.type.object.array.java",
"storage.type.primitive.array.java",
"storage.type.primitive.java",
"storage.type.token.java",
"storage.type.groovy",
"storage.type.annotation.groovy",
"storage.type.parameters.groovy",
"storage.type.generic.groovy",
"storage.type.object.array.groovy",
"storage.type.primitive.array.groovy",
"storage.type.primitive.groovy"
],
"settings": {
"foreground": "#4EC9B0"
}
},
{
"name": "Types declaration and references, TS grammar specific",
"scope": [
"meta.type.cast.expr",
"meta.type.new.expr",
"support.constant.math",
"support.constant.dom",
"support.constant.json",
"entity.other.inherited-class"
],
"settings": {
"foreground": "#4EC9B0"
}
},
{
"name": "Control flow / Special keywords",
"scope": [
"keyword.control",
"source.cpp keyword.operator.new",
"keyword.operator.delete",
"keyword.other.using",
"keyword.other.operator",
"entity.name.operator"
],
"settings": {
"foreground": "#C586C0"
}
},
{
"name": "Variable and parameter name",
"scope": [
"variable",
"meta.definition.variable.name",
"support.variable",
"entity.name.variable",
"constant.other.placeholder"
],
"settings": {
"foreground": "#9CDCFE"
}
},
{
"name": "Constants and enums",
"scope": ["variable.other.constant", "variable.other.enummember"],
"settings": {
"foreground": "#4FC1FF"
}
},
{
"name": "Object keys, TS grammar specific",
"scope": ["meta.object-literal.key"],
"settings": {
"foreground": "#9CDCFE"
}
},
{
"name": "CSS property value",
"scope": [
"support.constant.property-value",
"support.constant.font-name",
"support.constant.media-type",
"support.constant.media",
"constant.other.color.rgb-value",
"constant.other.rgb-value",
"support.constant.color"
],
"settings": {
"foreground": "#CE9178"
}
},
{
"name": "Regular expression groups",
"scope": [
"punctuation.definition.group.regexp",
"punctuation.definition.group.assertion.regexp",
"punctuation.definition.character-class.regexp",
"punctuation.character.set.begin.regexp",
"punctuation.character.set.end.regexp",
"keyword.operator.negation.regexp",
"support.other.parenthesis.regexp"
],
"settings": {
"foreground": "#CE9178"
}
},
{
"scope": [
"constant.character.character-class.regexp",
"constant.other.character-class.set.regexp",
"constant.other.character-class.regexp",
"constant.character.set.regexp"
],
"settings": {
"foreground": "#d16969"
}
},
{
"scope": ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"],
"settings": {
"foreground": "#DCDCAA"
}
},
{
"scope": "keyword.operator.quantifier.regexp",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "constant.character",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "constant.character.escape",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "entity.name.label",
"settings": {
"foreground": "#C8C8C8"
}
}
],
"semanticTokenColors": {
"newOperator": "#C586C0",
"stringLiteral": "#ce9178",
"customLiteral": "#DCDCAA",
"numberLiteral": "#b5cea8"
},
"colors": {
"checkbox.border": "#6B6B6B",
"editor.background": "#1E1E1E",
"editor.foreground": "#D4D4D4",
"editor.inactiveSelectionBackground": "#3A3D41",
"editorIndentGuide.background": "#404040",
"editorIndentGuide.activeBackground": "#707070",
"editor.selectionHighlightBackground": "#ADD6FF26",
"list.dropBackground": "#383B3D",
"activityBarBadge.background": "#007ACC",
"sideBarTitle.foreground": "#BBBBBB",
"input.placeholderForeground": "#A6A6A6",
"menu.background": "#252526",
"menu.foreground": "#CCCCCC",
"menu.separatorBackground": "#454545",
"menu.border": "#454545",
"statusBarItem.remoteForeground": "#FFF",
"statusBarItem.remoteBackground": "#16825D",
"ports.iconRunningProcessForeground": "#369432",
"sideBarSectionHeader.background": "#0000",
"sideBarSectionHeader.border": "#ccc3",
"tab.lastPinnedBorder": "#ccc3",
"list.activeSelectionIconForeground": "#FFF",
"terminal.inactiveSelectionBackground": "#3A3D41",
"widget.border": "#303031"
},
"type": "dark"
}

View File

@@ -1,3 +1,27 @@
export const BASE_URL = 'https://discord.js.org/docs/packages' as const;
export const BASE_URL_LEGACY = 'https://old.discordjs.dev/#/docs/discord.js' as const;
export const DESCRIPTION = 'Imagine a guide... that explores the many possibilities for your discord.js bot.';
export const GITHUB_BASE_PAGES_PATH = 'https://github.com/discordjs/discord.js/tree/main/apps/guide/src/pages';
export const PACKAGES = [
'discord.js',
'brokers',
'builders',
'collection',
'core',
'formatters',
'proxy',
'rest',
'next',
'util',
'voice',
'ws',
] as const;
/**
* The stable version of discord.js.
*/
export const VERSION = '14.9.0' as const;

View File

@@ -1,5 +1,5 @@
{
"extends": ["../../.eslintrc.json", "neon/react", "neon/next", "neon/edge", "neon/prettier"],
"extends": ["../../.eslintrc.json", "neon/react", "neon/next", "neon/edge", "@unocss", "neon/prettier"],
"settings": {
"react": {
"version": "detect"

View File

@@ -23,6 +23,7 @@ build/
src/styles/unocss.css
.next/
src/assets/readme/
static/
# Miscellaneous
.tmp/

View File

@@ -1,7 +1 @@
module.exports = {
...require('../../.prettierrc.json'),
plugins: [
'prettier-plugin-tailwindcss', // MUST come last
],
pluginSearchDirs: false,
};
module.exports = require('../../.prettierrc.json');

View File

@@ -1,4 +1,3 @@
import { fileURLToPath } from 'node:url';
import bundleAnalyzer from '@next/bundle-analyzer';
const withBundleAnalyzer = bundleAnalyzer({
@@ -7,18 +6,8 @@ const withBundleAnalyzer = bundleAnalyzer({
export default withBundleAnalyzer({
reactStrictMode: true,
eslint: {
ignoreDuringBuilds: true,
},
// Until Next.js fixes their type issues
typescript: {
ignoreBuildErrors: true,
},
outputFileTracing: true,
experimental: {
appDir: true,
outputFileTracingRoot: fileURLToPath(new URL('../../', import.meta.url)),
fallbackNodePolyfills: false,
serverComponentsExternalPackages: ['@microsoft/api-extractor-model', 'jju'],
},
images: {
@@ -33,6 +22,11 @@ export default withBundleAnalyzer({
destination: '/logo.svg',
permanent: true,
},
{
source: '/guide/:path*',
destination: 'https://next.discordjs.guide/guide/:path*',
permanent: true,
},
];
},
});

View File

@@ -50,62 +50,58 @@
"@discordjs/ui": "workspace:^",
"@microsoft/api-extractor-model": "7.26.4",
"@microsoft/tsdoc": "0.14.2",
"@planetscale/database": "^1.6.0",
"@planetscale/database": "1.7.0",
"@react-icons/all-files": "^4.1.0",
"@vercel/analytics": "^0.1.11",
"@vercel/edge-config": "^0.1.5",
"@vercel/og": "^0.5.0",
"@vercel/edge-config": "^0.1.7",
"@vercel/og": "^0.5.2",
"@vscode/codicons": "^0.0.32",
"ariakit": "^2.0.0-next.43",
"bright": "^0.7.0",
"ariakit": "^2.0.0-next.44",
"bright": "^0.7.1",
"cmdk": "^0.2.0",
"meilisearch": "^0.32.0",
"next": "^13.2.5-canary.23",
"meilisearch": "^0.32.3",
"next": "^13.3.1-canary.8",
"next-mdx-remote": "^4.4.1",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-custom-scrollbars-2": "^4.5.0",
"react-dom": "^18.2.0",
"react-use": "^17.4.0",
"rehype-ignore": "^1.0.4",
"rehype-ignore": "^1.0.5",
"rehype-raw": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark-gfm": "^3.0.1",
"server-only": "^0.0.1",
"sharp": "^0.32.0",
"swr": "^2.1.1"
"swr": "^2.1.3"
},
"devDependencies": {
"@next/bundle-analyzer": "^13.2.4",
"@next/bundle-analyzer": "^13.3.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/node": "18.15.11",
"@types/react": "^18.0.31",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"@unocss/cli": "^0.50.6",
"@unocss/reset": "^0.50.6",
"@unocss/cli": "^0.51.4",
"@unocss/eslint-config": "^0.51.4",
"@unocss/reset": "^0.51.4",
"@vitejs/plugin-react": "^3.1.0",
"@vitest/coverage-c8": "^0.29.8",
"@vitest/coverage-c8": "^0.30.1",
"concurrently": "^8.0.1",
"cpy-cli": "^4.2.0",
"cross-env": "^7.0.3",
"eslint": "^8.37.0",
"eslint-config-neon": "^0.1.41",
"eslint": "^8.38.0",
"eslint-config-neon": "^0.1.42",
"eslint-formatter-pretty": "^5.0.0",
"happy-dom": "^8.9.0",
"lighthouse": "^10.1.0",
"happy-dom": "^9.7.1",
"lighthouse": "^10.1.1",
"prettier": "^2.8.7",
"prettier-plugin-tailwindcss": "^0.2.6",
"typescript": "^5.0.3",
"unocss": "^0.50.6",
"vercel": "^28.18.3",
"typescript": "^5.0.4",
"unocss": "^0.51.4",
"vercel": "^28.18.5",
"vitest": "^0.29.8"
},
"engines": {
"node": ">=18.13.0"
},
"nextBundleAnalysis": {
"budget": 358400,
"budgetPercentIncreaseRed": 20,
"showDetails": true
}
}

View File

@@ -1,28 +1,29 @@
import { get } from '@vercel/edge-config';
import { NextResponse } from 'next/server';
import type { ServerRuntime } from 'next/types';
export const runtime: ServerRuntime = 'edge';
export const runtime = 'edge';
export async function GET() {
const url = await get<string>('DISCORD_WEBHOOK_URL');
const imageUrl = await get<string>('IT_IS_WEDNESDAY_MY_DUDES');
if (url && imageUrl) {
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'It is wednesday, my dudes',
embeds: [
{
image: {
url: imageUrl,
try {
const url = await get<string>('DISCORD_WEBHOOK_URL');
const imageUrl = await get<string>('IT_IS_WEDNESDAY_MY_DUDES');
if (url && imageUrl) {
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'It is wednesday, my dudes',
embeds: [
{
image: {
url: imageUrl,
},
},
},
],
}),
});
}
],
}),
});
}
} catch {}
return NextResponse.json({ message: 'It is wednesday, my dudes' });
}

View File

@@ -3,9 +3,8 @@
import type { ApiItemKind } from '@microsoft/api-extractor-model';
import { ImageResponse } from '@vercel/og';
import type { NextRequest } from 'next/server';
import type { ServerRuntime } from 'next/types';
export const runtime: ServerRuntime = 'edge';
export const runtime = 'edge';
const fonts = Promise.all([
fetch(new URL('../../../assets/fonts/Inter-Regular.ttf', import.meta.url)).then(async (res) => res.arrayBuffer()),

View File

@@ -1,9 +1,8 @@
/* eslint-disable react/no-unknown-property */
import { ImageResponse } from '@vercel/og';
import type { ServerRuntime } from 'next/types';
export const runtime: ServerRuntime = 'edge';
export const runtime = 'edge';
const fonts = fetch(new URL('../../../assets/fonts/Inter-Black.ttf', import.meta.url)).then(async (res) =>
res.arrayBuffer(),

View File

@@ -1,9 +1,16 @@
import 'server-only';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { connect } from '@planetscale/database';
import { cache } from 'react';
const sql = connect({ url: process.env.DATABASE_URL! });
const sql = connect({
async fetch(input, init) {
// @ts-expect-error: Deleting cache or setting as undefined, same thing
return fetch(input, { ...init, cache: undefined, next: { revalidate: 3_600 } });
},
url: process.env.DATABASE_URL!,
});
export async function fetchVersions(packageName: string): Promise<string[]> {
const response = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName}`, {
@@ -13,7 +20,7 @@ export async function fetchVersions(packageName: string): Promise<string[]> {
return response.json();
}
export const fetchModelJSON = cache(async (packageName: string, version: string): Promise<unknown> => {
export async function fetchModelJSON(packageName: string, version: string): Promise<unknown> {
if (process.env.NEXT_PUBLIC_LOCAL_DEV) {
const res = await readFile(
join(process.cwd(), '..', '..', 'packages', packageName, 'docs', 'docs.api.json'),
@@ -35,4 +42,4 @@ export const fetchModelJSON = cache(async (packageName: string, version: string)
// @ts-expect-error: https://github.com/planetscale/database-js/issues/71
return rows[0].data;
});
}

View File

@@ -1 +0,0 @@
export { default } from '~/app/loading';

View File

@@ -0,0 +1,22 @@
'use client';
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 (
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">404</h1>
<h2 className="text-[2rem] md:text-[3rem]">Not found.</h2>
<Link
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-blurple px-6 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href={href}
>
Take me back
</Link>
</div>
);
}

View File

@@ -12,10 +12,11 @@ import type {
ApiPropertySignature,
ApiTypeAlias,
ApiVariable,
ApiFunction,
} from '@microsoft/api-extractor-model';
import { ApiItemKind, ApiModel, ApiFunction } from '@microsoft/api-extractor-model';
import { ApiItemKind, ApiModel } from '@microsoft/api-extractor-model';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import type { Metadata } from 'next/types';
import { fetchModelJSON } from '~/app/docAPI';
import { Class } from '~/components/model/Class';
import { Interface } from '~/components/model/Interface';
@@ -23,35 +24,29 @@ 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, PACKAGES } from '~/util/constants';
import { findMember, findMemberByKey } from '~/util/model.server';
import { OVERLOAD_SEPARATOR } from '~/util/constants';
import type { ItemRouteParams } from '~/util/fetchMember';
import { fetchMember } from '~/util/fetchMember';
import { findMember } from '~/util/model';
export interface ItemRouteParams {
item: string;
package: string;
version: string;
}
async function fetchHeadMember({ package: packageName, version, item }: ItemRouteParams): Promise<ApiItem | undefined> {
async function fetchHeadMember({ package: packageName, version, item }: ItemRouteParams) {
const modelJSON = await fetchModelJSON(packageName, version);
const model = addPackageToModel(new ApiModel(), 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): URLSearchParams {
function resolveMemberSearchParams(packageName: string, member?: ApiItem) {
const params = new URLSearchParams({
pkg: packageName,
kind: member?.kind,
name: member?.displayName,
kind: member?.kind ?? '',
name: member?.displayName ?? '',
});
switch (member?.kind) {
@@ -84,11 +79,8 @@ function resolveMemberSearchParams(packageName: string, member: ApiItem): URLSea
return params;
}
// eslint-disable-next-line unicorn/numeric-separators-style
export const revalidate = 3600;
export async function generateMetadata({ params }: { params: ItemRouteParams }) {
const member = (await fetchHeadMember(params))!;
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');
@@ -120,39 +112,10 @@ export async function generateStaticParams({ params: { package: packageName, ver
}
return entry.members.map((member: ApiItem) => ({
item: member.displayName,
item: `${member.displayName}${OVERLOAD_SEPARATOR}${member.kind}`,
}));
}
async function fetchMember({ package: packageName, version: branchName = 'main', item }: ItemRouteParams) {
if (!PACKAGES.includes(packageName)) {
notFound();
}
const model = new ApiModel();
if (branchName === 'main') {
const modelJSONFiles = await Promise.all(PACKAGES.map(async (pkg) => fetchModelJSON(pkg, branchName)));
for (const modelJSONFile of modelJSONFiles) {
addPackageToModel(model, modelJSONFile);
}
} else {
const modelJSON = await fetchModelJSON(packageName, branchName);
addPackageToModel(model, modelJSON);
}
const [memberName, overloadIndex] = decodeURIComponent(item).split(OVERLOAD_SEPARATOR);
// eslint-disable-next-line prefer-const
let { containerKey, displayName: name } = findMember(model, packageName, memberName) ?? {};
if (name && overloadIndex && !Number.isNaN(Number.parseInt(overloadIndex, 10))) {
containerKey = ApiFunction.getContainerKey(name, Number.parseInt(overloadIndex, 10));
}
return memberName && containerKey ? findMemberByKey(model, packageName, containerKey) ?? null : null;
}
function Member({ member }: { member?: ApiItem }) {
switch (member?.kind) {
case 'Class':
@@ -175,5 +138,13 @@ function Member({ member }: { member?: ApiItem }) {
export default async function Page({ params }: { params: ItemRouteParams }) {
const member = await fetchMember(params);
return <div className="relative top-6">{member ? <Member member={member} /> : null}</div>;
if (!member) {
notFound();
}
return (
<div className="relative top-6">
<Member member={member} />
</div>
);
}

View File

@@ -4,7 +4,7 @@ export default function Error({ error }: { error: Error }) {
console.error(error);
return (
<div className="mx-auto flex h-full max-w-lg flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<div className="mx-auto h-full max-w-lg flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">500</h1>
<h2 className="text-[2rem] md:text-[3rem]">Error.</h2>
</div>

View File

@@ -6,6 +6,7 @@ import { notFound } from 'next/navigation';
import type { PropsWithChildren } from 'react';
import { Providers } from './providers';
import { fetchModelJSON, fetchVersions } from '~/app/docAPI';
import { Banner } from '~/components/Banner';
import { CmdKDialog } from '~/components/CmdK';
import { Nav } from '~/components/Nav';
import type { SidebarSectionItemData } from '~/components/Sidebar';
@@ -20,9 +21,6 @@ export interface VersionRouteParams {
version: string;
}
// eslint-disable-next-line unicorn/numeric-separators-style
export const revalidate = 3600;
export async function generateStaticParams() {
const params: VersionRouteParams[] = [];
@@ -72,14 +70,15 @@ export default async function PackageLayout({ children, params }: PropsWithChild
return (
<Providers>
<Banner className="mb-6" />
<main className="mx-auto max-w-7xl px-4 lg:max-w-full">
<Header />
<div className="relative top-6 mx-auto max-w-7xl gap-6 lg:flex lg:max-w-full">
<div className="lg:top-23 lg:sticky lg:h-[calc(100vh_-_100px)]">
<div className="relative top-2.5 mx-auto max-w-7xl gap-6 lg:max-w-full lg:flex">
<div className="lg:sticky lg:top-23 lg:h-[calc(100vh_-_145px)]">
<Nav members={members.map((member) => serializeIntoSidebarItemData(member))} />
</div>
<div className="min-w-xs mx-auto w-full max-w-5xl pb-10">
<div className="mx-auto max-w-5xl min-w-xs w-full pb-10">
{children}
<Footer />
</div>

View File

@@ -1 +0,0 @@
export { default } from '~/app/loading';

View File

@@ -27,7 +27,7 @@ export default async function Page({ params }: { params: VersionRouteParams }) {
const readmeSource = await loadREADME(packageName);
return (
<div className="prose max-w-none">
<div className="max-w-none prose">
{/* @ts-expect-error async component */}
<MDXRemote components={{ pre: SyntaxHighlighter }} options={mdxOptions} source={readmeSource} />
</div>

View File

@@ -3,10 +3,9 @@ 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 type { ServerRuntime } from 'next/types';
import { PACKAGES } from '~/util/constants';
export const runtime: ServerRuntime = 'edge';
export const runtime = 'edge';
async function getData(pkg: string) {
if (!PACKAGES.includes(pkg)) {
@@ -27,12 +26,12 @@ export default async function Page({ params }: { params: { package: string } })
const data = await getData(params.package);
return (
<div className="min-w-xs sm:w-md mx-auto flex min-h-screen flex-col gap-8 px-4 py-6 lg:px-6 lg:py-6">
<div className="mx-auto min-h-screen min-w-xs flex flex-col gap-8 px-4 py-6 sm:w-md lg:px-6 lg:py-6">
<h1 className="text-2xl font-semibold">Select a version:</h1>
<div className="flex flex-col gap-4">
{data.map((version, idx) => (
<Link
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 focus:ring-width-2 focus:ring-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-col place-content-center rounded border border-neutral-300 bg-white p-4 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 focus:ring active:translate-y-px active:bg-neutral-200 dark:text-white"
className="h-11 flex flex-col transform-gpu cursor-pointer select-none appearance-none place-content-center border border-neutral-300 rounded bg-white p-4 text-base font-semibold leading-none text-black outline-none active:translate-y-px dark:border-dark-100 active:bg-neutral-200 dark:bg-dark-400 hover:bg-neutral-100 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
href={`/docs/packages/${params.package}/${version}`}
key={`${version}-${idx}`}
>
@@ -47,7 +46,7 @@ export default async function Page({ params }: { params: { package: string } })
)) ?? null}
</div>
<Link
className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center gap-2 place-self-center rounded border-0 px-4 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px"
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center place-self-center gap-2 border-0 rounded bg-blurple px-4 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href="/docs/packages"
>
<VscArrowLeft size={20} /> Go back

View File

@@ -3,18 +3,17 @@ 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 type { ServerRuntime } from 'next/types';
import { PACKAGES } from '~/util/constants';
export const runtime: ServerRuntime = 'edge';
export const runtime = 'edge';
export default function Page() {
return (
<div className="min-w-xs sm:w-md mx-auto flex min-h-screen flex-col gap-8 px-4 py-6 lg:px-6 lg:py-6">
<div className="mx-auto min-h-screen min-w-xs flex flex-col gap-8 px-4 py-6 sm:w-md lg:px-6 lg:py-6">
<h1 className="text-2xl font-semibold">Select a package:</h1>
<div className="flex flex-col gap-4">
<a
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 focus:ring-width-2 focus:ring-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none place-content-between rounded border border-neutral-300 bg-white p-4 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 focus:ring active:translate-y-px active:bg-neutral-200 dark:text-white"
className="h-11 flex transform-gpu cursor-pointer select-none appearance-none place-content-between border border-neutral-300 rounded bg-white p-4 text-base font-semibold leading-none text-black outline-none active:translate-y-px dark:border-dark-100 active:bg-neutral-200 dark:bg-dark-400 hover:bg-neutral-100 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
href="https://old.discordjs.dev/#/docs/discord.js"
>
<div className="flex grow flex-row place-content-between place-items-center gap-4">
@@ -29,7 +28,7 @@ export default function Page() {
</a>
{PACKAGES.map((pkg, idx) => (
<Link
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 focus:ring-width-2 focus:ring-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-content-between rounded border border-neutral-300 bg-white p-4 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 focus:ring active:translate-y-px active:bg-neutral-200 dark:text-white"
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-content-between border border-neutral-300 rounded bg-white p-4 text-base font-semibold leading-none text-black outline-none active:translate-y-px dark:border-dark-100 active:bg-neutral-200 dark:bg-dark-400 hover:bg-neutral-100 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
href={`/docs/packages/${pkg}`}
key={`${pkg}-${idx}`}
>
@@ -41,7 +40,7 @@ export default function Page() {
</div>
{/* <Link href={`/docs/packages/${pkg}`}>
<div
className="bg-blurple focus:ring-width-2 flex h-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-content-center place-items-center rounded border-0 px-2 text-xs font-semibold leading-none text-white outline-0 focus:ring focus:ring-white active:translate-y-px"
className="bg-blurple focus:ring-width-2 flex h-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-content-center place-items-center rounded border-0 px-2 text-xs font-semibold leading-none text-white outline-none focus:ring focus:ring-white active:translate-y-px"
role="link"
>
Select version
@@ -53,7 +52,7 @@ export default function Page() {
</Link>
))}
<a
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 focus:ring-width-2 focus:ring-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none place-content-between rounded border border-neutral-300 bg-white p-4 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 focus:ring active:translate-y-px active:bg-neutral-200 dark:text-white"
className="h-11 flex transform-gpu cursor-pointer select-none appearance-none place-content-between border border-neutral-300 rounded bg-white p-4 text-base font-semibold leading-none text-black outline-none active:translate-y-px dark:border-dark-100 active:bg-neutral-200 dark:bg-dark-400 hover:bg-neutral-100 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
href="https://discord-api-types.dev/"
>
<div className="flex grow flex-row place-content-between place-items-center gap-4">
@@ -68,7 +67,7 @@ export default function Page() {
</a>
</div>
<Link
className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center gap-2 place-self-center rounded border-0 px-4 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px"
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center place-self-center gap-2 border-0 rounded bg-blurple px-4 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href="/"
>
<VscArrowLeft size={20} /> Go back

View File

@@ -4,7 +4,7 @@ export default function Error({ error }: { error: Error }) {
console.error(error);
return (
<div className="mx-auto flex min-h-screen max-w-lg flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">500</h1>
<h2 className="text-[2rem] md:text-[3rem]">Error.</h2>
</div>

View File

@@ -8,10 +8,10 @@ export default function GlobalError({ error }: { error: Error }) {
return (
<html className={inter.variable} lang="en" suppressHydrationWarning>
<body className="dark:bg-dark-800 bg-light-600">
<body className="bg-light-600 dark:bg-dark-600 dark:text-light-900">
<Providers>
<main className="mx-auto min-h-screen max-w-2xl">
<div className="mx-auto flex min-h-screen max-w-lg flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<main className="mx-auto max-w-2xl min-h-screen">
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">500</h1>
<h2 className="text-[2rem] md:text-[3rem]">Error.</h2>
</div>

View File

@@ -1,5 +1,5 @@
import { Analytics } from '@vercel/analytics/react';
import type { Metadata } from 'next/types';
import type { Metadata } from 'next';
import type { PropsWithChildren } from 'react';
import { Providers } from './providers';
import { DESCRIPTION } from '~/util/constants';
@@ -44,7 +44,7 @@ export const metadata: Metadata = {
themeColor: [
{ media: '(prefers-color-scheme: light)', color: '#f1f3f5' },
{ media: '(prefers-color-scheme: dark)', color: '#181818' },
{ media: '(prefers-color-scheme: dark)', color: '#1c1c1e' },
],
colorScheme: 'light dark',
@@ -68,14 +68,14 @@ export const metadata: Metadata = {
},
other: {
'msapplication-TileColor': '#090a16',
'msapplication-TileColor': '#1c1c1e',
},
};
export default function RootLayout({ children }: PropsWithChildren) {
return (
<html className={`${inter.variable} ${jetBrainsMono.variable}`} lang="en" suppressHydrationWarning>
<body className="dark:bg-dark-800 bg-light-600">
<body className="bg-light-600 dark:bg-dark-600 dark:text-light-900">
<Providers>{children}</Providers>
<Analytics />
</body>

View File

@@ -1,6 +1,6 @@
export default function Loading() {
return (
<div className="mx-4 flex min-h-screen flex-col items-center justify-center gap-4">
<div className="mx-4 min-h-screen flex flex-col items-center justify-center gap-4">
<svg
className="h-9 w-9 animate-spin text-black dark:text-white"
fill="none"

View File

@@ -2,12 +2,12 @@ import Link from 'next/link';
export default function NotFound() {
return (
<div className="mx-auto flex min-h-screen max-w-lg flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">404</h1>
<h2 className="text-[2rem] md:text-[3rem]">Not found.</h2>
<Link
className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded border-0 px-6 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px"
href="/docs/packages"
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-blurple px-6 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href="/docs"
>
Take me back
</Link>

View File

@@ -2,67 +2,70 @@ import { FiExternalLink } from '@react-icons/all-files/fi/FiExternalLink';
import Image from 'next/image';
import Link from 'next/link';
import vercelLogo from '~/assets/powered-by-vercel.svg';
import { SyntaxHighlighter } from '~/components/SyntaxHighlighter';
import { CODE_EXAMPLE } from '~/util/constants';
import { Banner } from '~/components/Banner';
import { InstallButton } from '~/components/InstallButton';
import { DESCRIPTION } from '~/util/constants';
export default function Page() {
return (
<div className="mx-auto flex min-h-screen max-w-6xl flex-col place-items-center gap-12 px-8 py-16 lg:place-content-center lg:px-8 lg:py-0">
<div className="flex flex-col place-items-center gap-10 lg:flex-row lg:gap-6">
<div className="flex max-w-lg flex-col gap-3 lg:mr-8">
<h1 className="text-3xl font-black leading-tight sm:text-5xl sm:leading-tight">
The <span className="bg-blurple relative rounded px-3 py-1 text-white">most popular</span> way to build
Discord <br /> bots.
</h1>
<p className="my-6 leading-normal text-neutral-700 dark:text-neutral-300">
discord.js is a powerful node.js module that allows you to interact with the Discord API very easily. It
takes a much more object-oriented approach than most other JS Discord libraries, making your bot&apos;s code
significantly tidier and easier to comprehend.
</p>
<div className="flex flex-row gap-4">
<Link
className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded border-0 px-6 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px"
href="/docs"
>
Docs
</Link>
<a
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 border-light-900 hover:bg-light-200 active:bg-light-300 focus:ring-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center gap-2 rounded border bg-white px-4 text-base font-semibold leading-none text-black no-underline outline-0 focus:ring active:translate-y-px dark:text-white"
href="https://discordjs.guide"
rel="noopener noreferrer"
target="_blank"
>
Guide <FiExternalLink />
</a>
<a
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 border-light-900 hover:bg-light-200 active:bg-light-300 focus:ring-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none appearance-none flex-row place-items-center gap-2 rounded border bg-white px-4 text-base font-semibold leading-none text-black no-underline outline-0 focus:ring active:translate-y-px dark:text-white"
href="https://github.com/discordjs/discord.js"
rel="noopener noreferrer"
target="_blank"
>
GitHub <FiExternalLink />
</a>
<div className="min-h-screen">
<Banner />
<div className="mx-auto max-w-6xl flex flex-col place-items-center gap-24 px-8 pb-16 pt-12 lg:min-h-[calc(100vh_-_40px)] lg:place-content-center lg:py-10">
<div className="flex flex-col place-items-center gap-10 lg:flex-row lg:gap-6">
<div className="flex flex-col place-items-center gap-8 text-center">
<h1 className="text-3xl font-black leading-tight sm:text-7xl sm:leading-tight">
The <span className="relative rounded bg-blurple px-3 py-1 text-white">most popular</span> way to build
Discord bots.
</h1>
<p className="my-6 leading-normal text-neutral-700 dark:text-neutral-300">{DESCRIPTION}</p>
<div className="flex flex-row gap-4">
<Link
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-blurple px-6 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href="/docs"
>
Docs
</Link>
{/* <Link
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-blurple px-6 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href="/guide"
>
Guide
</Link> */}
<a
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 border border-light-900 rounded bg-white px-4 text-base font-semibold leading-none text-black no-underline outline-none transition duration-200 active:translate-y-px dark:border-dark-100 hover:border-black active:bg-light-300 dark:bg-dark-400 hover:bg-light-200 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
href="https://discordjs.guide"
rel="noopener noreferrer"
target="_blank"
>
Guide <FiExternalLink />
</a>
<a
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none appearance-none place-items-center gap-2 border border-light-900 rounded bg-white px-4 text-base font-semibold leading-none text-black no-underline outline-none transition duration-200 active:translate-y-px dark:border-dark-100 hover:border-black active:bg-light-300 dark:bg-dark-400 hover:bg-light-200 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
href="https://github.com/discordjs/discord.js"
rel="external noopener noreferrer"
target="_blank"
>
GitHub <FiExternalLink />
</a>
</div>
<InstallButton />
</div>
</div>
<div className="max-w-xs sm:max-w-6xl">
{/* @ts-expect-error async component */}
<SyntaxHighlighter code={CODE_EXAMPLE} />
</div>
</div>
<div className="flex flex-row place-content-center">
<a
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
rel="noopener noreferrer"
rel="external noopener noreferrer"
target="_blank"
title="Vercel"
>
<Image
alt="Vercel"
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAABLCAQAAAA1k5H2AAAAi0lEQVR42u3SMQEAAAgDoC251a3gL2SgmfBYBRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAgwWEOSWBnYbKggAAAABJRU5ErkJggg=="
height={44}
placeholder="blur"
priority
src={vercelLogo}
width={212}
/>
</a>
</div>

View File

@@ -2,15 +2,12 @@
import { ThemeProvider } from 'next-themes';
import type { PropsWithChildren } from 'react';
import { ServiceWorker } from '~/components/ServiceWorker';
import { SystemThemeFallback } from '~/components/SystemThemeFallback';
import { useSystemThemeFallback } from '~/hooks/useSystemThemeFallback';
import { useUnregisterServiceWorker } from '~/hooks/useUnregisterServiceWorker';
export function Providers({ children }: PropsWithChildren) {
return (
<>
<ThemeProvider attribute="class">{children}</ThemeProvider>
<ServiceWorker />
<SystemThemeFallback />
</>
);
useUnregisterServiceWorker();
useSystemThemeFallback();
return <ThemeProvider attribute="class">{children}</ThemeProvider>;
}

View File

@@ -2,7 +2,7 @@ import { FiLink } from '@react-icons/all-files/fi/FiLink';
export function Anchor({ href }: { href: string }) {
return (
<a className="focus:ring-width-2 focus:ring-blurple mr-1 inline-block rounded outline-0 focus:ring" href={href}>
<a className="mr-1 inline-block rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple" href={href}>
<FiLink size={20} />
</a>
);

View File

@@ -0,0 +1,15 @@
import type { HTMLAttributes } from 'react';
export function Banner({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
return (
<div className={`bg-blurple px-4 py-2 text-center text-sm text-white ${className}`} {...props}>
You are reading the documentation for the <strong>next</strong> version of discord.js. Documentation for v13/v14+
has been moved to{' '}
<strong>
<a href="https://old.discordjs.dev/#/docs" rel="external noopener noreferrer" target="_blank">
old.discordjs.dev
</a>
</strong>
</div>
);
}

View File

@@ -50,7 +50,7 @@ export function CmdKDialog() {
() =>
searchResults?.map((item, idx) => (
<Command.Item
className="dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 [&[aria-selected]]:ring-blurple [&[aria-selected]]:ring-width-2 my-1 flex transform-gpu cursor-pointer select-none appearance-none flex-row place-content-center rounded bg-transparent px-4 py-2 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 active:translate-y-px active:bg-neutral-200 dark:text-white [&[aria-selected]]:ring"
className="my-1 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-content-center rounded bg-transparent px-4 py-2 text-base font-semibold leading-none text-black outline-none active:translate-y-px dark:border-dark-100 active:bg-neutral-200 hover:bg-neutral-100 dark:text-white [&[aria-selected]]:ring [&[aria-selected]]:ring-width-2 [&[aria-selected]]:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
key={`${item.id}-${idx}`}
onSelect={() => {
router.push(item.path);
@@ -60,10 +60,10 @@ export function CmdKDialog() {
<div className="flex grow flex-row place-content-between place-items-center gap-4">
<div className="flex flex-row place-items-center gap-4">
{resolveIcon(item.kind)}
<div className="w-50 sm:w-100 flex flex-col">
<div className="w-50 flex flex-col sm:w-100">
<h2 className="font-semibold">{item.name}</h2>
<div className="line-clamp-1 text-sm font-normal">{item.summary}</div>
<div className="line-clamp-1 hidden text-xs font-light opacity-75 dark:opacity-50 sm:block">
<div className="line-clamp-1 hidden text-xs font-light opacity-75 sm:block dark:opacity-50">
{item.path}
</div>
</div>
@@ -114,12 +114,12 @@ export function CmdKDialog() {
return (
<Dialog className="fixed left-1/2 top-1/4 z-50 -translate-x-1/2" state={dialog!}>
<Command
className="dark:bg-dark/50 min-w-xs sm:min-w-lg dark:border-dark-100 border-light-900 max-w-xs rounded border bg-white/50 shadow backdrop-blur-md sm:max-w-lg"
className="max-w-xs min-w-xs border border-light-900 rounded bg-white/50 shadow backdrop-blur-md sm:max-w-lg sm:min-w-lg dark:border-dark-100 dark:bg-dark/50"
label="Command Menu"
shouldFilter={false}
>
<Command.Input
className="dark:bg-dark/50 caret-blurple placeholder:text-dark-300/75 dark:border-dark-100 border-light-900 rounded-b-0 w-full rounded border-0 border-b bg-white/50 p-4 text-lg outline-0 dark:placeholder:text-white/75"
className="w-full border-0 border-b border-light-900 rounded rounded-b-0 bg-white/50 p-4 text-lg caret-blurple outline-none dark:border-dark-100 dark:bg-dark/50 placeholder:text-dark-300/75 dark:placeholder:text-white/75"
onValueChange={setSearch}
placeholder="Quick search..."
value={search}

View File

@@ -1,6 +1,8 @@
import type { ApiModel, Excerpt } from '@microsoft/api-extractor-model';
import { ExcerptTokenKind } from '@microsoft/api-extractor-model';
import { ItemLink } from './ItemLink';
import { resolveItemURI } from './documentation/util';
import { DISCORD_API_TYPES_DOCS_URL } from '~/util/constants';
export interface ExcerptTextProps {
/**
@@ -19,8 +21,24 @@ export interface ExcerptTextProps {
export function ExcerptText({ model, excerpt }: ExcerptTextProps) {
return (
<>
{excerpt.spannedTokens.map((token) => {
{excerpt.spannedTokens.map((token, idx) => {
if (token.kind === ExcerptTokenKind.Reference) {
const source = token.canonicalReference?.source;
if (source && 'packageName' in source && source.packageName === 'discord-api-types') {
const meaning = token.canonicalReference.symbol?.meaning;
const href =
meaning === 'type'
? `${DISCORD_API_TYPES_DOCS_URL}#${token.text}`
: `${DISCORD_API_TYPES_DOCS_URL}/${meaning}/${token.text}`;
return (
<a className="text-blurple" href={href} key={idx} rel="external noreferrer noopener" target="_blank">
{token.text}
</a>
);
}
const item = model.resolveDeclarationReference(token.canonicalReference!, model).resolvedApiItem;
if (!item) {
@@ -30,7 +48,7 @@ export function ExcerptText({ model, excerpt }: ExcerptTextProps) {
return (
<ItemLink
className="text-blurple"
itemURI={`${item.displayName}:${item.kind}`}
itemURI={resolveItemURI(item)}
key={`${item.displayName}-${item.containerKey}`}
packageName={item.getAssociatedPackage()?.displayName.replace('@discordjs/', '')}
>

View File

@@ -3,21 +3,23 @@ import vercelLogo from '~/assets/powered-by-vercel.svg';
export default function Footer() {
return (
<footer className="dark:bg-dark-800 bg-light-600 md:pl-12 md:pr-12">
<div className="mx-auto flex max-w-6xl flex-col place-items-center gap-12 pt-12 lg:place-content-center">
<div className="flex w-full flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
<footer className="md:pl-12 md:pr-12">
<div className="mx-auto max-w-6xl flex flex-col place-items-center gap-12 pt-12 lg:place-content-center">
<div className="w-full flex flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
<a
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
rel="noopener noreferrer"
rel="external noopener noreferrer"
target="_blank"
title="Vercel"
>
<Image
alt="Vercel"
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAABLCAQAAAA1k5H2AAAAi0lEQVR42u3SMQEAAAgDoC251a3gL2SgmfBYBRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAgwWEOSWBnYbKggAAAABJRU5ErkJggg=="
height={44}
placeholder="blur"
src={vercelLogo}
width={212}
/>
</a>
<div className="flex flex-row gap-6 md:gap-12">
@@ -25,17 +27,17 @@ export default function Footer() {
<div className="text-lg font-semibold">Community</div>
<div className="flex flex-col gap-1">
<a
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://discord.gg/djs"
rel="noopener noreferrer"
rel="external noopener noreferrer"
target="_blank"
>
Discord
</a>
<a
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://github.com/discordjs/discord.js/discussions"
rel="noopener noreferrer"
rel="external noopener noreferrer"
target="_blank"
>
GitHub discussions
@@ -46,15 +48,15 @@ export default function Footer() {
<div className="text-lg font-semibold">Project</div>
<div className="flex flex-col gap-1">
<a
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://github.com/discordjs/discord.js"
rel="noopener noreferrer"
rel="external noopener noreferrer"
target="_blank"
>
discord.js
</a>
<a
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://discordjs.guide"
rel="noopener noreferrer"
target="_blank"
@@ -62,9 +64,9 @@ export default function Footer() {
discord.js guide
</a>
<a
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://discord-api-types.dev"
rel="noopener noreferrer"
rel="external noopener noreferrer"
target="_blank"
>
discord-api-types

View File

@@ -26,7 +26,7 @@ export default function Header() {
.slice(1)
.map((path, idx, original) => (
<Link
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 hover:underline focus:ring"
className="rounded outline-none hover:underline focus:ring focus:ring-width-2 focus:ring-blurple"
href={`/${original.slice(0, idx + 1).join('/')}`}
key={`${path}-${idx}`}
>
@@ -43,7 +43,7 @@ export default function Header() {
return (
<Fragment key={`${el.key}-${idx}`}>
<div className="mx-2">/</div>
{el}
<div>{el}</div>
<div className="mx-2">/</div>
</Fragment>
);
@@ -52,49 +52,56 @@ export default function Header() {
if (idx !== array.length - 1) {
return (
<Fragment key={`${el.key}-${idx}`}>
{el}
<div>{el}</div>
<div className="mx-2">/</div>
</Fragment>
);
}
return <Fragment key={`${el.key}-${idx}`}>{el}</Fragment>;
return <div key={`${el.key}-${idx}`}>{el}</div>;
}),
[pathElements],
);
return (
<header className="dark:bg-dark/50 dark:border-dark-100 border-light-900 sticky top-4 z-20 rounded-md border bg-white/50 shadow backdrop-blur-md">
<header className="sticky top-4 z-20 border border-light-900 rounded-md bg-white/75 shadow backdrop-blur-md dark:border-dark-100 dark:bg-dark-600/75">
<div className="block h-16 px-6">
<div className="flex h-full flex-row place-content-between place-items-center gap-8">
<div className="h-full flex flex-row place-content-between place-items-center gap-8">
<Button
aria-label="Menu"
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px lg:hidden"
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none lg:hidden active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
onClick={() => setOpened((open) => !open)}
>
<VscMenu size={24} />
</Button>
<div className="hidden lg:flex lg:flex-row lg:overflow-hidden">{breadcrumbs}</div>
<div className="hidden lg:flex lg:grow lg:flex-row lg:overflow-hidden">{breadcrumbs}</div>
<Button
as="div"
className="dark:bg-dark-800 focus:ring-width-2 focus:ring-blurple w-56 grow rounded bg-white px-4 py-2.5 outline-0 focus:ring sm:grow-0"
className="hidden w-56 grow rounded bg-white px-4 py-2.5 outline-none md:block sm:grow-0 dark:bg-dark-800 focus:ring focus:ring-width-2 focus:ring-blurple"
onClick={() => dialog?.toggle()}
>
<div className="flex flex-row place-items-center gap-4 md:justify-between">
<VscSearch size={18} />
<span className="opacity-65">Search...</span>
<div className="md:opacity-65 hidden md:flex md:flex-row md:place-items-center md:gap-2">
<div className="hidden md:flex md:flex-row md:place-items-center md:gap-2 md:opacity-65">
<FiCommand size={18} /> K
</div>
</div>
</Button>
<div className="flex flex-row place-items-center gap-4">
<Button
as="div"
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none md:hidden active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
onClick={() => dialog?.toggle()}
>
<VscSearch size={24} />
</Button>
<Button
aria-label="GitHub"
as="a"
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded rounded-full border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px"
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded rounded-full bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://github.com/discordjs/discord.js"
rel="noopener noreferrer"
rel="external noopener noreferrer"
target="_blank"
>
<VscGithubInverted size={24} />

View File

@@ -7,7 +7,7 @@ export function InheritanceText({ parent }: { parent: ApiDeclaredItem }) {
<span className="font-semibold">
Inherited from{' '}
<ItemLink
className="text-blurple focus:ring-width-2 focus:ring-blurple rounded font-mono outline-0 focus:ring"
className="rounded font-mono text-blurple outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
itemURI={resolveItemURI(parent)}
>
{parent.displayName}

View File

@@ -0,0 +1,34 @@
'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';
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 (
<button
className="cursor-copy select-none bg-transparent px-4 py-2 text-sm text-dark-50 dark:text-light-900"
onClick={() => {
setInteracted(true);
copyToClipboard('npm install discord.js');
}}
type="button"
>
<span className="font-semibold text-blurple">{'>'}</span> npm install discord.js{' '}
{state.value && interacted ? (
<FiCheck className="ml-1 inline-block text-green-500" />
) : (
<FiCopy className="ml-1 inline-block" />
)}
</button>
);
}

View File

@@ -3,13 +3,13 @@
import type { LinkProps } from 'next/link';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import type { PropsWithChildren } from 'react';
import type { AnchorHTMLAttributes, PropsWithChildren, RefAttributes } from 'react';
import { useCurrentPathMeta } from '~/hooks/useCurrentPathMeta';
export interface ItemLinkProps
extends Omit<LinkProps, 'href'>,
React.RefAttributes<HTMLAnchorElement>,
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps> {
RefAttributes<HTMLAnchorElement>,
Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps> {
className?: string;
/**

View File

@@ -14,7 +14,7 @@ export function Nav({ members }: { members: SidebarSectionItemData[] }) {
return (
<nav
className={`dark:bg-dark/75 dark:border-dark-100 border-light-900 top-22 fixed bottom-4 left-4 right-4 z-20 mx-auto max-w-5xl rounded-md border bg-white/75 shadow backdrop-blur-md ${
className={`dark:bg-dark-600/75 dark:border-dark-100 border-light-900 top-22 fixed bottom-4 left-4 right-4 z-20 mx-auto max-w-5xl rounded-md border bg-white/75 shadow backdrop-blur-md ${
opened ? 'block' : 'hidden'
} lg:min-w-xs lg:sticky lg:block lg:h-full lg:w-full lg:max-w-xs`}
>
@@ -22,7 +22,7 @@ export function Nav({ members }: { members: SidebarSectionItemData[] }) {
autoHide
className="[&>div]:overscroll-none"
hideTracksWhenNotNeeded
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
renderTrackVertical={(props) => (
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
)}

View File

@@ -6,11 +6,11 @@ import { TableOfContentItems } from './TableOfContentItems';
export function Outline({ members }: { members: TableOfContentsSerialized[] }) {
return (
<aside className="dark:bg-dark-600 dark:border-dark-100 border-light-800 fixed bottom-0 right-0 top-[50px] z-20 hidden h-[calc(100vh_-_65px)] w-64 border-l bg-white pr-2 xl:block">
<aside className="fixed bottom-0 right-0 top-[50px] z-20 hidden h-[calc(100vh_-_65px)] w-64 border-l border-light-800 bg-white pr-2 xl:block dark:border-dark-100 dark:bg-dark-600">
<Scrollbars
autoHide
hideTracksWhenNotNeeded
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
renderTrackVertical={(props) => (
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
)}

View File

@@ -19,7 +19,7 @@ export default function OverloadSwitcher({ overloads, children }: PropsWithChild
() =>
overloads.map((_, idx) => (
<MenuItem
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple my-0.5 cursor-pointer rounded bg-white p-3 text-sm outline-0 focus:ring"
className="my-0.5 cursor-pointer rounded bg-white p-3 text-sm outline-none active:bg-light-800 dark:bg-dark-600 hover:bg-light-700 focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-400 dark:hover:bg-dark-500"
key={idx}
onClick={() => setOverloadIndex(idx + 1)}
>
@@ -32,7 +32,7 @@ export default function OverloadSwitcher({ overloads, children }: PropsWithChild
return (
<div className="flex flex-col place-items-start gap-2">
<MenuButton
className="bg-light-700 hover:bg-light-800 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-0 focus:ring"
className="mb-2 rounded bg-white p-3 outline-none active:bg-light-900 dark:bg-dark-400 hover:bg-light-800 focus:ring focus:ring-width-2 focus:ring-blurple md:-ml-2 dark:active:bg-dark-200 dark:hover:bg-dark-300"
state={menu}
>
<div className="flex flex-row place-content-between place-items-center gap-2">
@@ -48,7 +48,7 @@ export default function OverloadSwitcher({ overloads, children }: PropsWithChild
</div>
</MenuButton>
<Menu
className="dark:bg-dark-600 border-light-800 dark:border-dark-100 focus:ring-width-2 focus:ring-blurple z-20 flex flex-col rounded border bg-white p-1 outline-0 focus:ring"
className="z-20 flex flex-col border border-light-800 rounded bg-white p-1 outline-none dark:border-dark-100 dark:bg-dark-600 focus:ring focus:ring-width-2 focus:ring-blurple"
state={menu}
>
{menuItems}

View File

@@ -1,3 +1,5 @@
'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';
@@ -16,7 +18,7 @@ export default function PackageSelect() {
() => [
<a href="https://old.discordjs.dev/#/docs/discord.js" key="discord.js">
<MenuItem
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple my-0.5 rounded bg-white p-3 text-sm outline-0 focus:ring"
className="my-0.5 rounded bg-white p-3 text-sm outline-none active:bg-light-800 dark:bg-dark-600 hover:bg-light-700 focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-400 dark:hover:bg-dark-500"
id="discord-js"
onClick={() => packageMenu.setOpen(false)}
state={packageMenu}
@@ -27,7 +29,7 @@ export default function PackageSelect() {
...PACKAGES.map((pkg, idx) => (
<Link href={`/docs/packages/${pkg}/main`} key={`${pkg}-${idx}`}>
<MenuItem
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple my-0.5 rounded bg-white p-3 text-sm outline-0 focus:ring"
className="my-0.5 rounded bg-white p-3 text-sm outline-none active:bg-light-800 dark:bg-dark-600 hover:bg-light-700 focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-400 dark:hover:bg-dark-500"
id={pkg}
onClick={() => packageMenu.setOpen(false)}
state={packageMenu}
@@ -43,7 +45,7 @@ export default function PackageSelect() {
return (
<>
<MenuButton
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-0 focus:ring"
className="rounded bg-light-600 p-3 outline-none active:bg-light-800 dark:bg-dark-400 hover:bg-light-700 focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-400 dark:hover:bg-dark-300"
state={packageMenu}
>
<div className="flex flex-row place-content-between place-items-center">
@@ -58,7 +60,7 @@ export default function PackageSelect() {
</div>
</MenuButton>
<Menu
className="dark:bg-dark-600 border-light-800 dark:border-dark-100 focus:ring-width-2 focus:ring-blurple z-20 flex flex-col rounded border bg-white p-1 outline-0 focus:ring"
className="z-20 flex flex-col border border-light-800 rounded bg-white p-1 outline-none dark:border-dark-100 dark:bg-dark-600 focus:ring focus:ring-width-2 focus:ring-blurple"
state={packageMenu}
>
{packageMenuItems}

View File

@@ -4,7 +4,7 @@ export function Panel({ children }: PropsWithChildren) {
return (
<>
{children}
<div className="border-light-900 dark:border-dark-100 border-t-2" />
<div className="border-t-2 border-light-900 dark:border-dark-100" />
</>
);
}

View File

@@ -1,4 +1,9 @@
import type { ApiDeclaredItem, ApiItemContainerMixin, ApiPropertyItem } from '@microsoft/api-extractor-model';
import type {
ApiDeclaredItem,
ApiItemContainerMixin,
ApiProperty,
ApiPropertySignature,
} from '@microsoft/api-extractor-model';
import type { PropsWithChildren } from 'react';
import { Anchor } from './Anchor';
import { ExcerptText } from './ExcerptText';
@@ -17,29 +22,34 @@ export function Property({
inheritedFrom,
}: PropsWithChildren<{
inheritedFrom?: (ApiDeclaredItem & ApiItemContainerMixin) | undefined;
item: ApiPropertyItem;
item: ApiProperty | ApiPropertySignature;
separator?: PropertySeparatorType;
}>) {
const isDeprecated = Boolean(item.tsdocComment?.deprecatedBlock);
const hasSummary = Boolean(item.tsdocComment?.summarySection);
return (
<div className="scroll-mt-30 flex flex-col gap-4" id={item.displayName}>
<div className="flex flex-col scroll-mt-30 gap-4" id={item.displayName}>
<div className="flex flex-col gap-2 md:-ml-9">
{isDeprecated || item.isReadonly || item.isOptional ? (
{isDeprecated || item.isReadonly || item.isOptional || (item as ApiProperty).isStatic ? (
<div className="flex flex-row gap-1 md:ml-7">
{isDeprecated ? (
<div className="flex h-5 flex-row place-content-center place-items-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
<div className="h-5 flex flex-row place-content-center place-items-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
Deprecated
</div>
) : null}
{(item as ApiProperty).isStatic ? (
<div className="h-5 flex flex-row place-content-center place-items-center rounded-full bg-blurple px-3 text-center text-xs font-semibold uppercase text-white">
Static
</div>
) : null}
{item.isReadonly ? (
<div className="bg-blurple flex h-5 flex-row place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
<div className="h-5 flex flex-row place-content-center place-items-center rounded-full bg-blurple px-3 text-center text-xs font-semibold uppercase text-white">
Readonly
</div>
) : null}
{item.isOptional ? (
<div className="bg-blurple flex h-5 flex-row place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
<div className="h-5 flex flex-row place-content-center place-items-center rounded-full bg-blurple px-3 text-center text-xs font-semibold uppercase text-white">
Optional
</div>
) : null}
@@ -51,10 +61,14 @@ export function Property({
{item.displayName}
{item.isOptional ? '?' : ''}
</h4>
<h4 className="font-mono text-lg font-bold">{separator}</h4>
<h4 className="break-all font-mono text-lg font-bold">
<ExcerptText excerpt={item.propertyTypeExcerpt} model={item.getAssociatedModel()!} />
</h4>
{item.propertyTypeExcerpt.text ? (
<>
<h4 className="font-mono text-lg font-bold">{separator}</h4>
<h4 className="break-all font-mono text-lg font-bold">
<ExcerptText excerpt={item.propertyTypeExcerpt} model={item.getAssociatedModel()!} />
</h4>
</>
) : null}
</div>
</div>
{hasSummary || inheritedFrom ? (

View File

@@ -3,7 +3,6 @@ import type {
ApiItem,
ApiItemContainerMixin,
ApiProperty,
ApiPropertyItem,
ApiPropertySignature,
} from '@microsoft/api-extractor-model';
import { ApiItemKind } from '@microsoft/api-extractor-model';
@@ -25,10 +24,10 @@ export function PropertyList({ item }: { item: ApiItemContainerMixin }) {
<Fragment key={`${prop.item.displayName}-${idx}`}>
<Property
inheritedFrom={prop.inherited as ApiDeclaredItem & ApiItemContainerMixin}
item={prop.item as ApiPropertyItem}
item={prop.item as ApiProperty}
separator={PropertySeparatorType.Type}
/>
<div className="border-light-900 dark:border-dark-100 border-t-2" />
<div className="border-t-2 border-light-900 dark:border-dark-100" />
</Fragment>
);
}),

View File

@@ -2,15 +2,7 @@
import { Section as DJSSection, type SectionOptions } from '@discordjs/ui';
import type { PropsWithChildren } from 'react';
import { useMedia } from 'react-use';
// This is wrapper around the Section component from @discordjs/ui,
// it simply automatically sets the dense prop to true if the screen
// width is less than 768px. This is done to separate client-side logic
// from server-side rendering.
export function Section(options: PropsWithChildren<SectionOptions>) {
const matches = useMedia('(max-width: 768px)', true);
const modifiedOptions = { ...options, dense: matches };
return <DJSSection {...modifiedOptions} />;
return <DJSSection {...options} />;
}

View File

@@ -1,9 +0,0 @@
'use client';
import { useUnregisterServiceWorker } from '~/hooks/useUnregisterServiceWorker';
export function ServiceWorker() {
useUnregisterServiceWorker();
return null;
}

View File

@@ -7,7 +7,7 @@ import { VscSymbolField } from '@react-icons/all-files/vsc/VscSymbolField';
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 { usePathname } from 'next/navigation';
import { useSelectedLayoutSegment } from 'next/navigation';
import { useMemo } from 'react';
import { ItemLink } from './ItemLink';
import { Section } from './Section';
@@ -83,7 +83,7 @@ function resolveIcon(item: string) {
}
export function Sidebar({ members }: { members: SidebarSectionItemData[] }) {
const pathname = usePathname();
const segment = useSelectedLayoutSegment();
const { setOpened } = useNav();
const groupItems = useMemo(() => groupMembers(members), [members]);
@@ -93,11 +93,16 @@ export function Sidebar({ members }: { members: SidebarSectionItemData[] }) {
{(Object.keys(groupItems) as (keyof GroupedMembers)[])
.filter((group) => groupItems[group].length)
.map((group, idx) => (
<Section icon={resolveIcon(group)} key={`${group}-${idx}`} title={group}>
<Section
buttonClassName="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-none focus:ring"
icon={resolveIcon(group)}
key={`${group}-${idx}`}
title={group}
>
{groupItems[group].map((member, index) => (
<ItemLink
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-0 focus:rounded focus:border-0 focus:ring ${
pathname === member.href
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-none focus:rounded focus:border-0 focus:ring ${
decodeURIComponent(segment ?? '') === member.href
? 'bg-blurple text-white'
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
}`}

Some files were not shown because too many files have changed in this diff Show More