142 Commits

Author SHA1 Message Date
7b3d8e7b0e Update dependency @types/node to v20.16.11 2024-10-10 03:01:20 +00:00
Lee
96ab998031 Merge pull request 'Update dependency react-use-websocket to v4.9.0' (#68) from renovate/react-use-websocket-4.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 2m52s
Reviewed-on: #68
2024-10-10 02:17:12 +00:00
Lee
7de2848f45 Merge pull request 'Update dependency lucide-react to ^0.451.0' (#70) from renovate/lucide-monorepo into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #70
2024-10-10 02:17:07 +00:00
Lee
63fead44c3 Merge pull request 'Update nextjs monorepo to v14.2.15' (#72) from renovate/nextjs-monorepo into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #72
2024-10-10 02:16:48 +00:00
f8aa41ed05 Update nextjs monorepo to v14.2.15 2024-10-10 02:01:40 +00:00
50e6506fc4 Update dependency lucide-react to ^0.451.0 2024-10-08 10:01:35 +00:00
2a6488d4e5 Update dependency react-use-websocket to v4.9.0 2024-10-01 20:03:34 +00:00
Lee
7be126fe00 Merge pull request 'Update dependency lucide-react to ^0.446.0' (#64) from renovate/lucide-monorepo into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 1m44s
Reviewed-on: #64
2024-09-25 23:49:23 +00:00
0ef741381c Update dependency lucide-react to ^0.446.0 2024-09-25 06:01:24 +00:00
Lee
51ac278329 Merge pull request 'Update dependency postcss to v8.4.41' (#61) from renovate/postcss-8.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 44s
Reviewed-on: #61
2024-08-07 07:46:28 +00:00
Lee
9996df2f92 Merge pull request 'Update dependency @sentry/nextjs to v8.24.0' (#60) from renovate/sentry-javascript-monorepo into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #60
2024-08-07 07:46:19 +00:00
c1c74ffef7 Update dependency postcss to v8.4.41 2024-08-06 00:03:26 +00:00
cf0e595cfc Update dependency @sentry/nextjs to v8.23.0 2024-08-05 15:01:21 +00:00
Lee
2efcb4a780 Merge pull request 'Update dependency lucide-react to ^0.424.0' (#58) from renovate/lucide-react-0.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 1m12s
Reviewed-on: #58
2024-08-02 17:51:33 +00:00
Lee
50037d9090 Merge pull request 'Update dependency @types/node to v20.14.14' (#59) from renovate/node-20.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #59
2024-08-02 17:51:22 +00:00
8a1263a1dd Update dependency lucide-react to ^0.424.0 2024-08-02 11:01:18 +00:00
137de19133 Update dependency @types/node to v20.14.14 2024-08-02 09:01:18 +00:00
Lee
c9c79c1f29 Merge pull request 'Update dependency lucide-react to ^0.419.0' (#57) from renovate/lucide-react-0.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 45s
Reviewed-on: #57
2024-08-01 17:28:56 +00:00
65d0cc7807 Update dependency lucide-react to ^0.419.0 2024-08-01 14:01:26 +00:00
Lee
d44ace3f20 Merge pull request 'Update dependency @sentry/nextjs to v8.22.0' (#56) from renovate/sentry-javascript-monorepo into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 48s
Reviewed-on: #56
2024-08-01 13:17:37 +00:00
5b251e07ad Update dependency @sentry/nextjs to v8.22.0 2024-08-01 09:01:21 +00:00
Lee
636b0dde29 Merge pull request 'Update dependency lucide-react to ^0.418.0' (#55) from renovate/lucide-react-0.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 1m51s
Reviewed-on: #55
2024-08-01 01:26:18 +00:00
634cf8877a Update dependency lucide-react to ^0.418.0 2024-07-31 17:00:57 +00:00
Lee
6ee34eaf44 Merge pull request 'Update dependency @sentry/nextjs to v8.21.0' (#54) from renovate/sentry-javascript-monorepo into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 1m8s
Reviewed-on: #54
2024-07-31 15:59:16 +00:00
d51215a781 Update dependency @sentry/nextjs to v8.21.0 2024-07-31 11:01:06 +00:00
Lee
22dba0bc90 Merge pull request 'Update radix-ui-primitives monorepo' (#53) from renovate/radix-ui-primitives-monorepo into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 2m46s
Reviewed-on: #53
2024-07-31 00:36:04 +00:00
78ff92eed3 Update radix-ui-primitives monorepo 2024-07-30 23:01:11 +00:00
Lee
7e73cc3e8c Merge pull request 'Update dependency typescript to v5.5.4' (#52) from renovate/typescript-5.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 6s
Reviewed-on: #52
2024-07-30 22:15:34 +00:00
Lee
49cae6930b Merge pull request 'Update dependency mcutils-library to v1.3.4' (#51) from renovate/mcutils-library-1.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #51
2024-07-30 22:15:21 +00:00
33ab92f579 Update dependency typescript to v5.5.4 2024-07-30 22:01:09 +00:00
db6af18475 Update dependency mcutils-library to v1.3.4 2024-07-30 22:01:00 +00:00
Lee
a2cd3d0cf5 Merge pull request 'Update dependency tailwind-merge to v2.4.0' (#50) from renovate/tailwind-merge-2.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #50
2024-07-30 21:05:08 +00:00
Lee
916ee81302 Merge pull request 'Update dependency lucide-react to ^0.417.0' (#49) from renovate/lucide-react-0.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #49
2024-07-30 21:05:03 +00:00
Lee
28aa81036f Merge pull request 'Update dependency postcss to v8.4.40' (#43) from renovate/postcss-8.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #43
2024-07-30 21:04:58 +00:00
Lee
5fde0f46c5 Merge pull request 'Update dependency @eslint/eslintrc to v3.1.0' (#40) from renovate/eslint-eslintrc-3.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #40
2024-07-30 21:04:53 +00:00
Lee
5a4056f113 Merge pull request 'Update dependency @types/node to v20.14.13' (#32) from renovate/node-20.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #32
2024-07-30 21:04:47 +00:00
08e270da6f Update dependency tailwind-merge to v2.4.0 2024-07-30 21:02:27 +00:00
b2fbcb6ebf Update dependency lucide-react to ^0.417.0 2024-07-30 21:02:18 +00:00
7b602d64f8 Update dependency @types/node to v20.14.13 2024-07-30 21:02:13 +00:00
4026b08b42 Update dependency @eslint/eslintrc to v3.1.0 2024-07-30 21:02:07 +00:00
d5b48e8bc9 Update dependency postcss to v8.4.40 2024-07-30 21:01:53 +00:00
d3886e4a39 fix
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 1m26s
2024-07-30 21:32:54 +01:00
94b81e0d69 Merge branch 'master' of https://git.fascinated.cc/MinecraftUtilities/Frontend
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 10s
2024-07-30 21:29:51 +01:00
35e786d9a5 sentry 2024-07-30 21:29:32 +01:00
Lee
9278a23f5a Merge pull request 'Update dependency @types/react-syntax-highlighter to v15.5.13' (#35) from renovate/react-syntax-highlighter-15.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 2m3s
Reviewed-on: #35
2024-07-30 20:23:07 +00:00
Lee
2954dd4955 Merge pull request 'Update dependency @types/react to v18.3.3' (#36) from renovate/react-monorepo into master
Some checks are pending
Deploy App / docker (ubuntu-latest) (push) Waiting to run
Reviewed-on: #36
2024-07-30 20:22:58 +00:00
Lee
0ce04fbc37 Merge pull request 'Update dependency @heroicons/react to v2.1.5' (#42) from renovate/heroicons-react-2.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #42
2024-07-30 20:22:45 +00:00
Lee
9930b8387f Merge pull request 'Update dependency unified to v11.0.5' (#44) from renovate/unified-11.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #44
2024-07-30 20:22:36 +00:00
Lee
9be11a628b Merge pull request 'Update dependency tailwindcss to v3.4.7' (#48) from renovate/tailwindcss-3.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #48
2024-07-30 20:22:25 +00:00
Lee
0afcd08f91 Merge pull request 'Update dependency mcutils-library to v1.3.3' (#47) from renovate/mcutils-library-1.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #47
2024-07-30 20:21:42 +00:00
d1b3b7e1fe Update dependency tailwindcss to v3.4.7 2024-07-30 20:21:19 +00:00
0ae2c2956a Update dependency mcutils-library to v1.3.3 2024-07-30 20:21:10 +00:00
35ad8b458b Update dependency @types/react-syntax-highlighter to v15.5.13 2024-07-30 20:21:04 +00:00
c7365df0bd Update dependency @types/react to v18.3.3 2024-07-30 20:20:58 +00:00
307551dc05 Update dependency @heroicons/react to v2.1.5 2024-07-30 20:20:49 +00:00
Lee
fc90fdd59b Merge pull request 'Update nextjs monorepo to v14.2.5' (#45) from renovate/nextjs-monorepo into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 1m17s
Reviewed-on: #45
2024-07-30 20:17:53 +00:00
829a0afea0 Update nextjs monorepo to v14.2.5 2024-07-30 20:17:40 +00:00
Lee
266ac15bb1 Merge pull request 'Update dependency react-spinners to ^0.14.0' (#46) from renovate/react-spinners-0.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 57s
Reviewed-on: #46
2024-07-30 20:10:14 +00:00
Lee
78987e0c55 Merge pull request 'Update dependency @hookform/resolvers to v3.9.0' (#39) from renovate/hookform-resolvers-3.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 1m13s
Reviewed-on: #39
2024-07-30 20:06:01 +00:00
65a154a5dc Update dependency @hookform/resolvers to v3.9.0 2024-07-09 14:01:42 +00:00
Lee
179b5eb9d2 revert 2a17ac2d3d6389170a60ce4782bb733f32413874
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 2m32s
revert Merge pull request 'Update dependency @sentry/nextjs to v8' (#38) from renovate/major-sentry-javascript-monorepo into master

Reviewed-on: #38
2024-07-09 13:02:48 +00:00
24f34ecd03 Update dependency react-spinners to ^0.14.0 2024-07-08 11:01:05 +00:00
8e655681a7 Update dependency unified to v11.0.5 2024-07-08 10:01:09 +00:00
Lee
91867bb718 Delete sentry.edge.config.ts
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 23s
2024-07-08 09:37:33 +00:00
Lee
2a17ac2d3d Merge pull request 'Update dependency @sentry/nextjs to v8' (#38) from renovate/major-sentry-javascript-monorepo into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 1m10s
Reviewed-on: #38
2024-07-08 09:32:03 +00:00
840711604b Update dependency @sentry/nextjs to v8 2024-07-08 09:31:22 +00:00
Lee
260dadda6d Merge pull request 'Update dependency remote-mdx to ^0.0.8' (#37) from renovate/remote-mdx-0.x into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m58s
Reviewed-on: #37
2024-07-08 09:26:35 +00:00
Lee
5370fd7cad Merge pull request 'Update dependency lucide-react to ^0.402.0' (#33) from renovate/lucide-react-0.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 6s
Reviewed-on: #33
2024-07-08 09:25:32 +00:00
Lee
725e0cd25d Merge pull request 'Update dependency tailwindcss to v3.4.4' (#41) from renovate/tailwindcss-3.x-lockfile into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #41
2024-07-08 09:23:24 +00:00
649141ece3 Update dependency lucide-react to ^0.402.0 2024-07-08 08:00:57 +00:00
390f1ffd64 Update dependency tailwindcss to v3.4.4 2024-06-05 18:01:39 +00:00
eb32d3786d Update dependency remote-mdx to ^0.0.8 2024-05-11 14:01:34 +00:00
Lee
bc27ed78b1 Merge pull request 'Update react monorepo to v18.3.1' (#31) from renovate/react-monorepo into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 3m11s
Reviewed-on: #31
2024-04-28 04:28:31 +00:00
11e2efef2e Update react monorepo to v18.3.1 2024-04-27 10:00:58 +00:00
Lee
68e0599159 Merge pull request 'Update dependency lucide-react to ^0.376.0' (#30) from renovate/lucide-react-0.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 4s
Reviewed-on: #30
2024-04-27 09:50:30 +00:00
Lee
bc04ac8e82 Merge pull request 'Update dependency remote-mdx to ^0.0.7' (#29) from renovate/remote-mdx-0.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #29
2024-04-27 09:49:27 +00:00
fe0a0d19f9 Update dependency lucide-react to ^0.376.0 2024-04-27 03:01:02 +00:00
cfdc69a078 Update dependency remote-mdx to ^0.0.7 2024-04-27 03:00:56 +00:00
Lee
9abf21e890 Merge pull request 'Update react monorepo to v18.3.1' (#28) from renovate/react-monorepo into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 3m2s
Reviewed-on: #28
2024-04-27 02:09:39 +00:00
2d3db00551 Update react monorepo to v18.3.0 2024-04-25 17:01:01 +00:00
Lee
73441e0898 Merge pull request 'Update nextjs monorepo to v14.2.3' (#27) from renovate/nextjs-monorepo into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 2m48s
Reviewed-on: #27
2024-04-24 20:28:55 +00:00
7bee4c1611 Update nextjs monorepo to v14.2.3 2024-04-24 20:02:16 +00:00
Lee
5fd3d2f822 Merge pull request 'Update dependency remark-mdx to v3.0.1' (#25) from renovate/mdx-monorepo into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 2m12s
Reviewed-on: #25
2024-04-24 16:29:12 +00:00
Lee
3156916b4f Merge pull request 'Update dependency @sentry/nextjs to v7.112.2' (#26) from renovate/sentry-javascript-monorepo into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 3m9s
Reviewed-on: #26
2024-04-24 16:24:43 +00:00
Lee
a29f0c41ac Merge pull request 'Update dependency lucide-react to ^0.373.0' (#24) from renovate/lucide-react-0.x into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 3m7s
Reviewed-on: #24
2024-04-24 16:20:42 +00:00
dbcba6682b Update dependency @sentry/nextjs to v7.112.2 2024-04-24 13:01:04 +00:00
cf1f2bce26 Update dependency remark-mdx to v3.0.1 2024-04-24 09:01:50 +00:00
795f02e0e0 Update dependency lucide-react to ^0.373.0 2024-04-24 08:01:01 +00:00
239d2f2078 Merge remote-tracking branch 'origin/master'
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m52s
2024-04-23 17:52:42 +01:00
30e55d43c7 use my paste server 2024-04-23 17:52:37 +01:00
Lee
62919e1b93 Merge pull request 'Update dependency @sentry/nextjs to v7.112.1' (#23) from renovate/sentry-javascript-monorepo into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 2m41s
Reviewed-on: #23
2024-04-23 14:56:22 +00:00
Lee
0ccc90851a Merge pull request 'Update dependency remote-mdx to ^0.0.5' (#22) from renovate/remote-mdx-0.x into master
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
Reviewed-on: #22
2024-04-23 14:56:07 +00:00
Lee
fb70dd6ab7 Merge pull request 'Update dependency clsx to v2.1.1' (#21) from renovate/clsx-2.x-lockfile into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 2m19s
Reviewed-on: #21
2024-04-23 14:51:07 +00:00
4198176b69 Update dependency @sentry/nextjs to v7.112.0 2024-04-23 10:00:59 +00:00
8001ad7a11 Update dependency remote-mdx to ^0.0.5 2024-04-23 06:01:05 +00:00
89805d0442 Update dependency clsx to v2.1.1 2024-04-23 06:00:57 +00:00
Lee
58f4a6edf8 Merge pull request 'Update dependency mcutils-library to v1.3.2' (#20) from renovate/mcutils-library-1.x-lockfile into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 4m39s
Reviewed-on: #20
2024-04-23 02:39:29 +00:00
a0b8777a94 fix the code in the code dialog falling off the dialog
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m32s
2024-04-22 22:56:34 +01:00
1e96ed1b1c add json upload button and add page reload button to server and player pages
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m29s
2024-04-22 22:50:13 +01:00
63e4eedc37 deadmau5
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m59s
2024-04-22 21:14:07 +01:00
c2790054db start work on cleaning up types and add try a player and server to their pages
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m29s
2024-04-22 21:08:44 +01:00
1ab27f69be Update dependency mcutils-library to v1.3.2 2024-04-22 20:01:02 +00:00
52fea3da68 use the server preview from the api in the server route for embeds
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 3m25s
2024-04-22 20:15:56 +01:00
Lee
4ffd10e9c3 Merge pull request 'Update dependency mcutils-library to v1.3.1' (#19) from renovate/mcutils-library-1.x-lockfile into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 3m2s
Reviewed-on: #19
2024-04-22 19:11:51 +00:00
9e17145e90 change docs and docs page
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m30s
2024-04-22 20:07:53 +01:00
782b5efd4b clear pages when clicking on a page in the command dialog
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m29s
2024-04-22 17:20:54 +01:00
e277645333 update the command menu
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m30s
2024-04-22 17:06:50 +01:00
9ba13d1160 testing the test
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 41s
2024-04-22 14:21:09 +01:00
40029b9839 ci
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m29s
2024-04-22 02:47:08 +01:00
ffa2db120f update error page
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
2024-04-22 02:41:42 +01:00
525b4981fe fix spacing on docs page and remove postbuild task in docker
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m25s
2024-04-22 02:38:06 +01:00
Lee
ce55389cba Delete public/sitemap.xml
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m34s
2024-04-22 01:31:17 +00:00
Lee
656eefa2e5 Delete public/robots.txt
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
2024-04-22 01:31:14 +00:00
230e5d6864 ci
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Has been cancelled
2024-04-22 02:31:02 +01:00
69fd6483de xi
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 51s
2024-04-22 02:29:22 +01:00
a6650af640 add the sitemap
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 9s
2024-04-22 02:28:35 +01:00
f0eaaaad5e fix command menu keyboard shortcuts
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m22s
2024-04-22 02:13:30 +01:00
96dc8de92f add language indicator to the code highlighter
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m32s
2024-04-22 02:11:41 +01:00
00a5febf66 fix code highlighter colors and centre the breadcrumbs on docs
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m23s
2024-04-22 01:56:01 +01:00
49daf6f1a4 impl command menu and change theme colors
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 3m26s
2024-04-22 01:21:04 +01:00
c2d7f5f33c Update dependency mcutils-library to v1.3.1 2024-04-21 23:01:03 +00:00
a200fa045c fix Dockerfile
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 2m15s
2024-04-21 20:22:17 +01:00
d26c70f507 fix Dockerfile
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 11s
2024-04-21 20:20:44 +01:00
0b9a112c5b maybe fix?
Some checks failed
Deploy App / docker (ubuntu-latest) (push) Failing after 40s
2024-04-21 20:18:01 +01:00
23c4284d8a add a release version to Sentry
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m25s
2024-04-21 20:08:17 +01:00
5c05493ed1 maybe it'll push release data, who knows
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m24s
2024-04-21 19:48:24 +01:00
Lee
32364ab58f Delete .env
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m25s
2024-04-21 18:42:37 +00:00
Lee
81e6580eed Merge pull request 'Update dependency @sentry/nextjs to v7.111.0' (#18) from renovate/sentry-javascript-monorepo into master
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 2m4s
Reviewed-on: #18
2024-04-21 18:05:39 +00:00
735d6532c2 Update dependency @sentry/nextjs to v7.111.0 2024-04-21 18:00:57 +00:00
2a06a4d338 attempt to impl sentry
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 2m1s
2024-04-21 18:23:29 +01:00
8d35e7ab0f cleanup imports 2024-04-21 17:39:56 +01:00
ec27cd9f29 add github icon and display breadcrumb on docs home page 2024-04-21 17:39:00 +01:00
738fcb8e24 add search icon to the docs search button 2024-04-21 17:13:46 +01:00
ab2716a8c9 fix not found page
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m27s
2024-04-21 13:50:02 +01:00
130444e3e2 update docs link 2024-04-21 13:49:52 +01:00
Lee
e791c20ba7 Update documentation/server/favicon.md
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m13s
2024-04-21 05:27:01 +00:00
96abad164a add padding spacing to the bottom of the doc page
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m14s
2024-04-21 06:05:25 +01:00
e6a28ed268 mostly completed docs
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m38s
2024-04-21 05:47:52 +01:00
3b1872a9dc update js lib doc 2024-04-21 02:44:47 +01:00
4b336e4456 add a continue typing indicator
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m6s
2024-04-21 02:38:16 +01:00
cb6814d47b update placeholder text
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m9s
2024-04-21 02:24:16 +01:00
389c004d16 fix mobile responsiveness for search dialog
All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 1m11s
2024-04-21 02:23:41 +01:00
64 changed files with 8776 additions and 4917 deletions

3
.eslintrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

16
.gitignore vendored
View File

@ -37,3 +37,19 @@ yarn-error.log*
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
# Sentry Config File
.sentryclirc
# Sentry Config File
.sentryclirc
# Environment variables
.env
# Sitemap & Robots
/public/sitemap*
/public/robots.txt
# Sentry Config File
.env.sentry-build-plugin

View File

@ -22,11 +22,18 @@ WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
COPY . . COPY . .
# Next.js collects completely anonymous telemetry data about general usage. # Get the git commit hash
# Learn more here: https://nextjs.org/telemetry ARG GIT_REV
# Uncomment the following line in case you want to disable telemetry during the build. ENV GIT_REV ${GIT_REV}
# Sentry Auth Token
ARG SENTRY_AUTH_TOKEN
ENV SENTRY_AUTH_TOKEN ${SENTRY_AUTH_TOKEN}
# Disable telemetry during build
ENV NEXT_TELEMETRY_DISABLED 1 ENV NEXT_TELEMETRY_DISABLED 1
# Build the frontend
RUN \ RUN \
if [ -f yarn.lock ]; then yarn run build; \ if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \ elif [ -f package-lock.json ]; then npm run build; \
@ -39,7 +46,8 @@ FROM base AS runner
WORKDIR /app WORKDIR /app
ENV NODE_ENV production ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# Disable telemetry during runtime
ENV NEXT_TELEMETRY_DISABLED 1 ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs RUN addgroup --system --gid 1001 nodejs

View File

@ -1,3 +1,3 @@
# Minecraft Utilities - Frontend # Minecraft Utilities - Frontend
See [The Website](https://mcutils.xyz) or [Minecraft Utilities Documentation](https://mcutils.xyz/documentation) for more information. See [The Website](https://mcutils.xyz) or [Minecraft Utilities Documentation](https://mcutils.xyz/docs) for more information.

View File

@ -1,11 +1,26 @@
--- ---
title: Home title: Home
summary: Welcome to the Minecraft Utilities documentation! Here you can find information on how to use the various features of the API. summary: Welcome to the Minecraft Utilities documentation!
--- ---
# Getting Started # Getting Started
This is still a work in progress, so please be patient as we continue to add more documentation. Welcome to the Minecraft Utilities documentation! You can find information on how to use the various features of the API.
## Features
See below for a list of features that are currently available in the API.
| Feature | Description |
|----------------------------------------------------------|----------------------------------------|
| [Player Lookup](/docs/player/player-lookup) | Get a player's information |
| [Player Username To Uuid](/docs/player/username-to-uuid) | Get a player's skin parts |
| [Player Skin Parts](/docs/player/skin-parts) | Get a player's skin parts |
| [Server Lookup](/docs/server/server-lookup) | Get a server's information |
| [Server Icon](/docs/server/favicon) | Get a server's icon |
| [Server Preview](/docs/server/preview) | View the server as if you were in-game |
| [Mojang Status](/docs/mojang/endpoint-status) | Get the status of Mojang's services |
## Libraries ## Libraries

View File

@ -22,7 +22,7 @@ This is a simple example of how to use the Javascript library to get a player's
```javascript ```javascript
import { getPlayer, CachedPlayer } from "mcutils-library"; import { getPlayer, CachedPlayer } from "mcutils-library";
const cachedPlayer = await getPlayer(playerId); const cachedPlayer: CachedPlayer = await getPlayer(playerId);
console.log(player); console.log(player);
``` ```

View File

@ -0,0 +1,63 @@
---
title: Mojang Endpoint Status
summary: Get the status of the Mojang APIs.
---
# Overview
The Mojang endpoint status endpoint allows you to get the status of the Mojang APIs.
## Endpoint
```
GET /mojang/status
```
## Example
```bash
curl -X GET "https://api.mcutils.xyz/mojang/status" -H "accept: application/json"
```
## Response
```json
{
"cache": {
"cached": false,
"cachedTime": -1
},
"endpoints": [
{
"name": "Minecraft Textures",
"hostname": "textures.minecraft.net",
"status": "ONLINE"
},
{
"name": "Minecraft Libraries",
"hostname": "libraries.minecraft.net",
"status": "ONLINE"
},
{
"name": "Minecraft Services",
"hostname": "api.minecraftservices.com",
"status": "ONLINE"
},
{
"name": "Mojang Assets",
"hostname": "assets.mojang.com",
"status": "ONLINE"
},
{
"name": "Mojang API",
"hostname": "api.mojang.com",
"status": "ONLINE"
},
{
"name": "Mojang Session Server",
"hostname": "sessionserver.mojang.com",
"status": "ONLINE"
}
]
}
```

View File

@ -0,0 +1,68 @@
---
title: Player Lookup
summary: Get information about a player.
---
# Overview
The player lookup endpoint allows you to get information about a player. This includes their UUID, username, and any other information that is available.
## Endpoint
```
GET /player/:query
```
## Parameters
| Parameter | Description | Required |
|-----------|---------------------------------------------------------|----------|
| query | The username or uuid of the player you want to look up. | Yes |
## Example
```bash
curl -X GET "https://api.mcutils.xyz/player/imfascinated" -H "accept: application/json"
```
## Response
```json
{
"cache": {
"cached": false,
"cachedTime": -1
},
"uniqueId": "eeab5f8a-18dd-4d58-af78-2b3c4543da48",
"trimmedUniqueId": "eeab5f8a18dd4d58af782b3c4543da48",
"username": "ImFascinated",
"skin": {
"url": "http://textures.minecraft.net/texture/ba1e0c9e21983c06e45614642316cd7029a297bc246bc6d236a41388c3ff9a09",
"model": "SLIM",
"legacy": false,
"parts": {
"head": "https://api.mcutils.xyz/player/head/eeab5f8a-18dd-4d58-af78-2b3c4543da48",
"face": "https://api.mcutils.xyz/player/face/eeab5f8a-18dd-4d58-af78-2b3c4543da48",
"body": "https://api.mcutils.xyz/player/body/eeab5f8a-18dd-4d58-af78-2b3c4543da48"
}
},
"cape": {
"url": "http://textures.minecraft.net/texture/2340c0e03dd24a11b15a8b33c2a7e9e32abb2051b2481d0ba7defd635ca7a933"
},
"rawProperties": [
{
"name": "textures",
"value": "ewogICJ0aW1lc3RhbXAiIDogMTcxMzY3MDc4MTM0NSwKICAicHJvZmlsZUlkIiA6ICJlZWFiNWY4YTE4ZGQ0ZDU4YWY3ODJiM2M0NTQzZGE0OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJJbUZhc2NpbmF0ZWQiLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmExZTBjOWUyMTk4M2MwNmU0NTYxNDY0MjMxNmNkNzAyOWEyOTdiYzI0NmJjNmQyMzZhNDEzODhjM2ZmOWEwOSIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9LAogICAgIkNBUEUiIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzIzNDBjMGUwM2RkMjRhMTFiMTVhOGIzM2MyYTdlOWUzMmFiYjIwNTFiMjQ4MWQwYmE3ZGVmZDYzNWNhN2E5MzMiCiAgICB9CiAgfQp9",
"signed": false
}
]
}
```
## Errors
| Status Code | Description |
|-------------|-----------------------------------------------|
| 400 | The username is invalid. |
| 404 | The player was not found. |
| 429 | The Mojang API rate limit has been exhausted. |

View File

@ -0,0 +1,47 @@
---
title: Player Skin Parts
summary: Get a specific part of a player's skin.
---
# Overview
The player skin parts endpoint allows you to get a specific part of a player's skin.
## Endpoint
```
GET /player/:part/:query
```
## Parts
| Part | Description |
|------|---------------------------------|
| head | Get the player's isometric head |
| face | Get the player's face |
| body | Get the player's body |
## Parameters
| Parameter | Description | Required |
|-----------|---------------------------------------------------------|----------|
| part | The part of the skin you want to get. | Yes |
| query | The username or uuid of the player you want to look up. | Yes |
## Example
```bash
curl -X GET "https://api.mcutils.xyz/player/head/imfascinated" -H "accept: image/png"
```
## Response
![Skin Part](https://api.mcutils.xyz/player/head/eeab5f8a-18dd-4d58-af78-2b3c4543da48 "Player's Head")
## Errors
| Status Code | Description |
|-------------|-----------------------------------------------|
| 400 | The username is invalid. |
| 404 | The player was not found. |
| 429 | The Mojang API rate limit has been exhausted. |

View File

@ -0,0 +1,46 @@
---
title: Player Username to UUID
summary: Get a player's UUID from their username.
---
# Overview
The player username to UUID endpoint allows you to get a player's UUID from their username.
## Endpoint
```
GET /player/uuid/:query
```
## Parameters
| Parameter | Description | Required |
|-----------|---------------------------------------------------------|----------|
| query | The username of the player you want to look up. | Yes |
## Example
```bash
curl -X GET "https://api.mcutils.xyz/player/uuid/imfascinated" -H "accept: application/json"
```
## Response
```json
{
"cache": {
"cached": false,
"cachedTime": -1
},
"uniqueId": "eeab5f8a-18dd-4d58-af78-2b3c4543da48",
"username": "ImFascinated"
}
```
## Errors
| Status Code | Description |
|-------------|-----------------------------------------------|
| 400 | The username is invalid. |
| 429 | The Mojang API rate limit has been exhausted. |

View File

@ -0,0 +1,45 @@
---
title: Server Blocked Status
summary: Get the Mojang blocked status of a Minecraft server.
---
# Overview
The server blocked status endpoint allows you to get the Mojang blocked status of a Minecraft server.
## Endpoint
```
GET /server/blocked/:query
```
## Platforms
This endpoint is only available for Java servers.
## Parameters
| Parameter | Description | Required |
|-----------|---------------------------------------------------------------|----------|
| query | The IP address or hostname of the server you want to look up. | Yes |
## Example
```bash
curl -X GET "https://api.mcutils.xyz/server/blocked/play.hypixel.net"
```
## Response
```json
{
"blocked": false
}
```
## Errors
| Status Code | Description |
|-------------|-----------------------------------------------|
| 400 | The server was not found. |
| 429 | The Mojang API rate limit has been exhausted. |

View File

@ -0,0 +1,41 @@
---
title: Server Favicon
summary: Get the favicon of a Minecraft server.
---
# Overview
The server favicon endpoint allows you to get the favicon of a Minecraft server.
## Endpoint
```
GET /server/icon/:query
```
## Platforms
This endpoint is only available for Java servers.
## Parameters
| Parameter | Description | Required |
|-----------|---------------------------------------------------------------------------|----------|
| query | The IP address or hostname of the server you want to get the favicon for. | Yes |
## Example
```bash
curl -X GET "https://api.mcutils.xyz/server/icon/hypixel.net" -H "accept: image/png"
```
## Response
![Server Icon](https://api.mcutils.xyz/server/icon/play.hypixel.net)
## Errors
| Status Code | Description |
|-------------|-----------------------------------------------|
| 400 | The server was not found. |
| 429 | The Mojang API rate limit has been exhausted. |

View File

@ -0,0 +1,45 @@
---
title: Server Preview
summary: Get the server list preview of a Minecraft server.
---
# Overview
The server preview endpoint allows you to get the server list preview of a Minecraft server.
## Endpoint
```
GET /server/:platform/preview/:query
```
## Platforms
| Platform | Description |
|----------|-----------------|
| bedrock | Bedrock Edition |
| java | Java Edition |
## Parameters
| Parameter | Description | Required |
|-----------|---------------------------------------------------------------------------|----------|
| platform | The platform of the server you want to get the preview for. | Yes |
| query | The IP address or hostname of the server you want to get the preview for. | Yes |
## Example
```bash
curl -X GET "https://api.mcutils.xyz/server/java/preview/hypixel.net" -H "accept: image/png"
```
## Response
![Server Icon](https://api.mcutils.xyz/server/java/preview/play.hypixel.net)
## Errors
| Status Code | Description |
|-------------|-----------------------------------------------|
| 400 | The server was not found. |
| 429 | The Mojang API rate limit has been exhausted. |

File diff suppressed because one or more lines are too long

5
next-sitemap.config.js Normal file
View File

@ -0,0 +1,5 @@
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: process.env.SITE_URL || "https://mcutils.xyz",
generateRobotsTxt: true,
};

View File

@ -1,4 +1,16 @@
import createMDX from "@next/mdx"; import {withSentryConfig} from "@sentry/nextjs";
import nextBuildId from "next-build-id";
import path from "path";
import { fileURLToPath } from "url";
/**
* The current git commit hash.
*
* @type {string|string} The current git commit hash.
*/
const buildId = (
process.env.GIT_REV || nextBuildId.sync({ dir: path.dirname(fileURLToPath(import.meta.url)) })
).substring(0, 7);
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
@ -17,6 +29,46 @@ const nextConfig = {
}, },
], ],
}, },
env: {
NEXT_PUBLIC_BUILD_ID: buildId,
},
experimental: {
mdxRs: true,
},
}; };
export default createMDX()(nextConfig) export default withSentryConfig(nextConfig, {
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options
org: "minecraft-utilities",
project: "frontend",
sentryUrl: "https://glitchtip.fascinated.cc/",
// Only print logs for uploading source maps in CI
silent: !process.env.CI,
// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,
// Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
// This can increase your server load as well as your hosting bill.
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// side errors will fail.
// tunnelRoute: "/monitoring",
// Hides source maps from generated client bundles
hideSourceMaps: true,
// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: true,
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
// See the following for more information:
// https://docs.sentry.io/product/crons/
// https://vercel.com/docs/cron-jobs
automaticVercelMonitors: true,
});

View File

@ -5,6 +5,7 @@
"scripts": { "scripts": {
"dev": "next dev --turbo", "dev": "next dev --turbo",
"build": "next build", "build": "next build",
"postbuild": "next-sitemap",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint"
}, },
@ -24,33 +25,46 @@
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-tooltip": "^1.0.7", "@radix-ui/react-tooltip": "^1.0.7",
"@sentry/nextjs": "^8.20.0",
"@types/mdx": "^2.0.13", "@types/mdx": "^2.0.13",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clipboard-copy": "^4.0.1", "clipboard-copy": "^4.0.1",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"cmdk": "^1.0.0",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"lucide-react": "^0.372.0", "lucide-react": "^0.451.0",
"mcutils-library": "^1.2.6", "mcutils-library": "^1.3.1",
"moment": "^2.30.1", "moment": "^2.30.1",
"next": "14.2.2", "next": "14.2.5",
"next-build-id": "^3.0.0",
"next-sitemap": "^4.2.3",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react": "^18", "react": "^18",
"react-countup": "^6.5.3", "react-countup": "^6.5.3",
"react-dom": "^18", "react-dom": "^18",
"react-spinners": "^0.13.8", "react-spinners": "^0.14.0",
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",
"react-use-websocket": "4.8.1", "react-use-websocket": "4.9.0",
"read-file": "^0.2.0", "read-file": "^0.2.0",
"remote-mdx": "^0.0.4", "remark-gfm": "^4.0.0",
"remote-mdx": "^0.0.8",
"tailwind-merge": "^2.2.2", "tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7" "tailwindcss-animate": "^1.0.7"
}, },
"pnpm": {
"overrides": {
"remark-mdx": "3.0.1",
"unified": "11.0.5",
"remark-parse": "11.0.0",
"mdast-util-frontmatter": "2.0.1"
}
},
"devDependencies": { "devDependencies": {
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"@types/react-syntax-highlighter": "^15.5.11", "@types/react-syntax-highlighter": "^15.5.11",
"eslint-config-next": "14.2.2", "eslint-config-next": "14.2.5",
"postcss": "^8", "postcss": "^8",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"typescript": "^5" "typescript": "^5"

11775
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
public/media/github.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

30
sentry.client.config.ts Normal file
View File

@ -0,0 +1,30 @@
// This file configures the initialization of Sentry on the client.
// The config you add here will be used whenever a users loads a page in their browser.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "https://25aaa031240f4d659649d28e0a3fb0cb@glitchtip.fascinated.cc/1",
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
replaysOnErrorSampleRate: 1.0,
// This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production
replaysSessionSampleRate: 0.1,
// You can remove this option if you're not planning to use the Sentry Session Replay feature:
integrations: [
Sentry.replayIntegration({
// Additional Replay configuration goes in here, for example:
maskAllText: true,
blockAllMedia: true,
}),
],
});

16
sentry.edge.config.ts Normal file
View File

@ -0,0 +1,16 @@
// This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on).
// The config you add here will be used whenever one of the edge features is loaded.
// Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "https://25aaa031240f4d659649d28e0a3fb0cb@glitchtip.fascinated.cc/1",
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
});

19
sentry.server.config.ts Normal file
View File

@ -0,0 +1,19 @@
// This file configures the initialization of Sentry on the server.
// The config you add here will be used whenever the server handles a request.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "https://25aaa031240f4d659649d28e0a3fb0cb@glitchtip.fascinated.cc/1",
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
// Uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: process.env.NODE_ENV === 'development',
});

View File

@ -2,16 +2,10 @@ import { CustomMDX } from "@/app/components/mdx-components";
import { Metadata } from "next"; import { Metadata } from "next";
import { generateEmbed } from "@/app/common/embed"; import { generateEmbed } from "@/app/common/embed";
import { Title } from "@/app/components/title"; import { Title } from "@/app/components/title";
import { getDocContent, getDocsContent } from "@/app/common/documentation"; import { DocsContentMetadata, getDocContent, getDocsContent } from "@/app/common/documentation";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
} from "@/app/components/ui/breadcrumb";
import { capitalizeFirstLetter } from "@/app/common/string-utils";
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";
import { GithubLink } from "@/app/components/docs/github-link";
import { DocsBreadcrumb } from "@/app/components/docs/breadcrumb";
type DocumentationPageParams = { type DocumentationPageParams = {
params: { params: {
@ -23,7 +17,7 @@ type DocumentationPageParams = {
}; };
export async function generateStaticParams() { export async function generateStaticParams() {
let documentationPages = getDocsContent(); let documentationPages: DocsContentMetadata[] = getDocsContent();
return documentationPages.map(page => ({ return documentationPages.map(page => ({
slug: [page.slug], slug: [page.slug],
@ -31,7 +25,7 @@ export async function generateStaticParams() {
} }
export async function generateMetadata({ params: { slug } }: DocumentationPageParams): Promise<Metadata> { export async function generateMetadata({ params: { slug } }: DocumentationPageParams): Promise<Metadata> {
const page = getDocContent(slug); const page: DocsContentMetadata | undefined = getDocContent(slug);
// Fallback to page not found // Fallback to page not found
if (!page) { if (!page) {
@ -48,38 +42,24 @@ export async function generateMetadata({ params: { slug } }: DocumentationPagePa
} }
export default function Page({ params: { slug } }: DocumentationPageParams) { export default function Page({ params: { slug } }: DocumentationPageParams) {
const page = getDocContent(slug); const page: DocsContentMetadata | undefined = getDocContent(slug);
// Page was not found, show an error page // Page was not found, show an error page
if (!page) { if (!page) {
return notFound(); return notFound();
} }
const slugParts = page.slug.split("/");
return ( return (
<div className="w-full h-full px-4 flex flex-col gap-4"> <div className="w-full h-full px-4 flex flex-col gap-2">
{slugParts.length > 1 && ( <div className="flex justify-between items-center">
<Breadcrumb> {/* The breadcrumb for the documentation page */}
<BreadcrumbList> <DocsBreadcrumb page={page} />
<BreadcrumbItem>
<BreadcrumbLink href="/docs">Home</BreadcrumbLink>
</BreadcrumbItem>
{slugParts.map((slug, index, array) => {
const path = array.slice(0, index + 1).join("/");
return ( {/* The Git link for the documentation page */}
<div key={slug} className="flex items-center "> <div className="flex flex-row gap-2 items-center">
<BreadcrumbSeparator className="pr-1.5" /> <GithubLink page={page} />
<BreadcrumbItem> </div>
<BreadcrumbLink href={`/docs/${path}`}>{capitalizeFirstLetter(slug)}</BreadcrumbLink>
</BreadcrumbItem>
</div> </div>
);
})}
</BreadcrumbList>
</Breadcrumb>
)}
{/* The documentation page title and description */} {/* The documentation page title and description */}
<div className="text-center mb-4"> <div className="text-center mb-4">
@ -87,7 +67,7 @@ export default function Page({ params: { slug } }: DocumentationPageParams) {
</div> </div>
{/* The content of the documentation page */} {/* The content of the documentation page */}
<div className="text-left w-full"> <div className="text-left w-full pb-[2rem]">
<CustomMDX source={page.content} /> <CustomMDX source={page.content} />
</div> </div>
</div> </div>

View File

@ -1,15 +0,0 @@
import React, { ReactElement } from "react";
import { Search } from "@/app/components/docs/search";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>): ReactElement {
return (
<div className="w-full flex flex-col items-center gap-2 h-full md:flex-row md:items-start">
<Search />
{children}
</div>
);
}

View File

@ -1,13 +1,13 @@
import { Card } from "@/app/components/card"; import { Colors } from "@/app/common/colors";
import { generateEmbed } from "@/app/common/embed"; import { generateEmbed } from "@/app/common/embed";
import { capitalizeFirstLetter } from "@/app/common/string-utils"; import { capitalizeFirstLetter } from "@/app/common/string-utils";
import { cn } from "@/app/common/utils"; import { cn } from "@/app/common/utils";
import { Card } from "@/app/components/card";
import { Title } from "@/app/components/title";
import { CachedEndpointStatus, getMojangEndpointStatus, Status } from "mcutils-library"; import { CachedEndpointStatus, getMojangEndpointStatus, Status } from "mcutils-library";
import { Metadata, Viewport } from "next"; import { Metadata, Viewport } from "next";
import Link from "next/link"; import Link from "next/link";
import { ReactElement } from "react"; import { ReactElement } from "react";
import { Colors } from "@/app/common/colors";
import { Title } from "@/app/components/title";
/** /**
* Force the page to be dynamic, so it will be regenerated on every request * Force the page to be dynamic, so it will be regenerated on every request
@ -89,20 +89,19 @@ export default async function Page(): Promise<ReactElement> {
> >
{endpoints.length == 0 && <p>Unable to fetch endpoint statuses</p>} {endpoints.length == 0 && <p>Unable to fetch endpoint statuses</p>}
{endpoints.length > 0 && {endpoints.length > 0 &&
endpoints.map((endpoint: CachedEndpointStatus) => { endpoints.map((server: CachedEndpointStatus) => {
const { name, hostname, status } = endpoint; const { name, endpoint, status } = server;
const url = `https://${hostname}`;
return ( return (
<div key={name} className="flex flex-row justify-between pt-2"> <div key={name} className="flex flex-row justify-between pt-2">
<div className="flex flex-col leading-[1.5rem]"> <div className="flex flex-col leading-[1.5rem]">
<p className="font-semibold">{name}</p> <p className="font-semibold">{name}</p>
<Link <Link
href={url} href={endpoint}
className="text-sm text-primary hover:opacity-75 transition-all transform-gpu" className="text-sm text-primary hover:opacity-75 transition-all transform-gpu"
target="_blank" target="_blank"
> >
<p>{url}</p> <p>{endpoint}</p>
</Link> </Link>
</div> </div>
<div className={cn("flex items-center font-semibold", getColor(status))}> <div className={cn("flex items-center font-semibold", getColor(status))}>

View File

@ -5,36 +5,9 @@ import { Button } from "../components/ui/button";
import { Separator } from "../components/ui/separator"; import { Separator } from "../components/ui/separator";
import { Tooltip, TooltipContent, TooltipTrigger } from "../components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "../components/ui/tooltip";
import { Title } from "@/app/components/title"; import { Title } from "@/app/components/title";
import { LandingButton } from "@/app/types/landing/landing-button";
type Button = { const buttons: LandingButton[] = [
/**
* The title of the button.
*/
title: string;
/**
* The tooltip to display for this statistic.
*/
tooltip: string;
/**
* The URL to go to.
*/
url: string;
/**
* Whether clicking the button will
* open the link in a new tab.
*/
openInNewTab?: boolean;
/**
* The class name to apply to the button.
*/
className?: string;
};
const buttons: Button[] = [
{ {
title: "Get Started", title: "Get Started",
tooltip: "Click to get started with the API", tooltip: "Click to get started with the API",

View File

@ -12,26 +12,19 @@ import { CachedPlayer, getPlayer, McUtilsAPIError } from "mcutils-library";
import { Metadata, Viewport } from "next"; import { Metadata, Viewport } from "next";
import { ReactElement } from "react"; import { ReactElement } from "react";
import { Title } from "@/app/components/title"; import { Title } from "@/app/components/title";
import { PlayerPageParams } from "@/app/types/player/page-params";
import { TryAPlayer } from "@/app/components/player/try-a-player";
/** export const revalidate = 60;
* Force the page to be dynamic, so it will be regenerated on every request
*/
export const revalidate = 0;
type Params = { export async function generateViewport({ params: { id } }: PlayerPageParams): Promise<Viewport> {
params: {
id: string;
};
};
export async function generateViewport({ params: { id } }: Params): Promise<Viewport> {
const validPlayer = await isValidPlayer(id); const validPlayer = await isValidPlayer(id);
return { return {
themeColor: validPlayer ? Colors.green : Colors.red, themeColor: validPlayer ? Colors.green : Colors.red,
}; };
} }
export async function generateMetadata({ params: { id } }: Params): Promise<Metadata> { export async function generateMetadata({ params: { id } }: PlayerPageParams): Promise<Metadata> {
// No id provided // No id provided
if (!id || id.length === 0) { if (!id || id.length === 0) {
return generateEmbed({ return generateEmbed({
@ -58,7 +51,7 @@ export async function generateMetadata({ params: { id } }: Params): Promise<Meta
} }
} }
export default async function Page({ params: { id } }: Params): Promise<ReactElement> { export default async function Page({ params: { id } }: PlayerPageParams): Promise<ReactElement> {
let error: string | undefined = undefined; // The error to display let error: string | undefined = undefined; // The error to display
let player: CachedPlayer | undefined = undefined; // The player to display let player: CachedPlayer | undefined = undefined; // The player to display
@ -101,6 +94,9 @@ export default async function Page({ params: { id } }: Params): Promise<ReactEle
</ContextMenuContent> </ContextMenuContent>
</ContextMenu> </ContextMenu>
)} )}
{/* Try a Player */}
{player == null && !error && <TryAPlayer />}
</div> </div>
); );
} }

View File

@ -5,7 +5,6 @@ import { ServerView } from "@/app/components/server/server-view";
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from "@/app/components/ui/context-menu"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from "@/app/components/ui/context-menu";
import { Colors } from "@/app/common/colors"; import { Colors } from "@/app/common/colors";
import { generateEmbed } from "@/app/common/embed"; import { generateEmbed } from "@/app/common/embed";
import { formatNumber } from "@/app/common/number-utils";
import { isValidServer } from "@/app/common/server"; import { isValidServer } from "@/app/common/server";
import { capitalizeFirstLetter } from "@/app/common/string-utils"; import { capitalizeFirstLetter } from "@/app/common/string-utils";
import config from "@root/config.json"; import config from "@root/config.json";
@ -19,18 +18,10 @@ import {
import { Metadata, Viewport } from "next"; import { Metadata, Viewport } from "next";
import { ReactElement } from "react"; import { ReactElement } from "react";
import { Title } from "@/app/components/title"; import { Title } from "@/app/components/title";
import { ServerPageParams } from "@/app/types/server/page-params";
import { TryAServer } from "@/app/components/server/try-a-server";
/** export const revalidate = 60;
* Force the page to be dynamic, so it will be regenerated on every request
*/
export const revalidate = 0;
type Params = {
params: {
platform: ServerPlatform;
hostname: string;
};
};
/** /**
* Gets the favicon for a server * Gets the favicon for a server
@ -60,14 +51,14 @@ function checkPlatform(platform: ServerPlatform): boolean {
return platform === ServerPlatform.Java || platform === ServerPlatform.Bedrock; return platform === ServerPlatform.Java || platform === ServerPlatform.Bedrock;
} }
export async function generateViewport({ params: { platform, hostname } }: Params): Promise<Viewport> { export async function generateViewport({ params: { platform, hostname } }: ServerPageParams): Promise<Viewport> {
const validPlayer = await isValidServer(platform, hostname); const validPlayer = await isValidServer(platform, hostname);
return { return {
themeColor: validPlayer || !platform ? Colors.green : Colors.red, themeColor: validPlayer || !platform ? Colors.green : Colors.red,
}; };
} }
export async function generateMetadata({ params: { platform, hostname } }: Params): Promise<Metadata> { export async function generateMetadata({ params: { platform, hostname } }: ServerPageParams): Promise<Metadata> {
if (!checkPlatform(platform)) { if (!checkPlatform(platform)) {
// Invalid platform // Invalid platform
return generateEmbed({ return generateEmbed({
@ -85,18 +76,14 @@ export async function generateMetadata({ params: { platform, hostname } }: Param
try { try {
const server = await getServer(platform, hostname); const server = await getServer(platform, hostname);
const { hostname: serverHostname, players } = server; const { hostname: serverHostname } = server;
const favicon = getFavicon(platform, server);
let description = `There is ${formatNumber(players.online)}/${formatNumber(players.max)} players connected!\n\n`;
description += "Click to view more information about the server.";
return generateEmbed({ return generateEmbed({
title: `${serverHostname} ${capitalizeFirstLetter(platform)} Server`, title: `${serverHostname} ${capitalizeFirstLetter(platform)} Server`,
embedTitle: `${capitalizeFirstLetter(platform)} Server: ${serverHostname}`, embedTitle: `${capitalizeFirstLetter(platform)} Server: ${serverHostname}`,
description: description, description: "Click to view more information about the server.",
image: favicon, image: `${config.apiEndpoint}/server/${platform}/preview/${serverHostname}`,
cardType: "summary_large_image",
}); });
} catch (err) { } catch (err) {
// An error occurred // An error occurred
@ -107,7 +94,7 @@ export async function generateMetadata({ params: { platform, hostname } }: Param
} }
} }
export default async function Page({ params: { platform, hostname } }: Params): Promise<ReactElement> { export default async function Page({ params: { platform, hostname } }: ServerPageParams): Promise<ReactElement> {
let error: string | undefined = undefined; // The error to display let error: string | undefined = undefined; // The error to display
let server: CachedJavaMinecraftServer | CachedBedrockMinecraftServer | undefined = undefined; // The server to display let server: CachedJavaMinecraftServer | CachedBedrockMinecraftServer | undefined = undefined; // The server to display
let invalidPlatform: boolean = !checkPlatform(platform); // Whether the platform is invalid let invalidPlatform: boolean = !checkPlatform(platform); // Whether the platform is invalid
@ -136,7 +123,10 @@ export default async function Page({ params: { platform, hostname } }: Params):
<LookupServer currentPlatform={platform.toLowerCase()} currentServer={hostname && hostname[0]} /> <LookupServer currentPlatform={platform.toLowerCase()} currentServer={hostname && hostname[0]} />
</div> </div>
{/* An errored occurred when looking up the server */}
{error && <ErrorCard message={error} />} {error && <ErrorCard message={error} />}
{/* The server */}
{server != null && ( {server != null && (
<ContextMenu> <ContextMenu>
<ContextMenuTrigger asChild> <ContextMenuTrigger asChild>
@ -154,6 +144,9 @@ export default async function Page({ params: { platform, hostname } }: Params):
</ContextMenuContent> </ContextMenuContent>
</ContextMenu> </ContextMenu>
)} )}
{/* Try a Server */}
{server == null && !error && <TryAServer />}
</div> </div>
); );
} }

View File

@ -11,11 +11,6 @@ export type DocsContentMetadata = MDXMetadata & {
*/ */
title: string; title: string;
/**
* The date this content was published.
*/
published: string;
/** /**
* The summary of this content. * The summary of this content.
*/ */
@ -111,7 +106,9 @@ export function getDocsContent(): DocsContentMetadata[] {
export function getDocContent(path?: string[]): DocsContentMetadata | undefined { export function getDocContent(path?: string[]): DocsContentMetadata | undefined {
const slug: string = path ? path.join("/") : "home"; const slug: string = path ? path.join("/") : "home";
return cachedDocs.find(doc => doc.slug === slug); return process.env.NODE_ENV === "development"
? getDocsContent().find((doc: DocsContentMetadata) => doc.slug === slug)
: cachedDocs.find((doc: DocsContentMetadata) => doc.slug === slug);
} }
/** /**
@ -122,7 +119,7 @@ export function getDocContent(path?: string[]): DocsContentMetadata | undefined
* @param limit the maximum number of results * @param limit the maximum number of results
*/ */
export function searchDocs( export function searchDocs(
query: string, query?: string,
limit?: number, limit?: number,
): { ): {
title: string; title: string;
@ -132,7 +129,7 @@ export function searchDocs(
if (!limit) { if (!limit) {
limit = 5; // Default to 5 results limit = 5; // Default to 5 results
} }
return fuseIndex.search(query, { limit }).map(result => { return fuseIndex.search(query || "", { limit }).map(result => {
return { return {
title: result.item.title, title: result.item.title,
summary: result.item.summary, summary: result.item.summary,

View File

@ -20,6 +20,11 @@ type Embed = {
* The image to show as the thumbmail. * The image to show as the thumbmail.
*/ */
image?: string; image?: string;
/**
* The type of the card.
*/
cardType?: "summary" | "summary_large_image";
}; };
/** /**
@ -29,13 +34,17 @@ type Embed = {
* @param embedTitle the title of the embed * @param embedTitle the title of the embed
* @param description the description of the embed * @param description the description of the embed
* @param image the image to show as the thumbmail * @param image the image to show as the thumbmail
* @param cardType the type of the card
* @returns the metadata for the embed * @returns the metadata for the embed
*/ */
export function generateEmbed({ title, embedTitle, description, image }: Embed): Metadata { export function generateEmbed({ title, embedTitle, description, image, cardType }: Embed): Metadata {
// Fall back to the title // Fall back to the title
if (!embedTitle) { if (!embedTitle) {
embedTitle = title; embedTitle = title;
} }
if (!cardType) {
cardType = "summary";
}
const metadata: Metadata = { const metadata: Metadata = {
title: `${title}`, title: `${title}`,
@ -44,7 +53,7 @@ export function generateEmbed({ title, embedTitle, description, image }: Embed):
description: description, description: description,
}, },
twitter: { twitter: {
card: "summary", card: cardType,
}, },
}; };

View File

@ -0,0 +1,17 @@
const PASTE_URL: string = "https://paste.fascinated.cc";
/**
* Creates a new haste with the given content.
*
* @param content the content to create the haste with
* @returns the URL of the created haste
*/
export async function createHaste(content: string): Promise<string> {
const response = await fetch(`${PASTE_URL}/api/upload`, {
method: "POST",
body: content,
});
const { id } = await response.json();
return `${PASTE_URL}/${id}`;
}

View File

@ -1,6 +1,15 @@
import { ReactElement } from "react"; import { ReactElement } from "react";
import { CodeHighlighter } from "./code-highlighter"; import { CodeHighlighter } from "./code-highlighter";
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog"; import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "./ui/dialog";
import { CreateHasteButton } from "@/app/components/create-haste-button";
type CodeDialogProps = { type CodeDialogProps = {
/** /**
@ -28,12 +37,15 @@ export function CodeDialog({ title, description, code, children }: CodeDialogPro
return ( return (
<Dialog> <Dialog>
<DialogTrigger asChild>{children}</DialogTrigger> <DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="max-h-screen md:max-h-[700px] text-sm"> <DialogContent className="text-sm">
<DialogHeader> <DialogHeader>
<DialogTitle>{title}</DialogTitle> <DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription> <DialogDescription>{description}</DialogDescription>
</DialogHeader> </DialogHeader>
<CodeHighlighter code={code} /> <CodeHighlighter code={code} />
<DialogFooter>
<CreateHasteButton content={code} />
</DialogFooter>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );

View File

@ -2,6 +2,8 @@ import { ReactElement } from "react";
import SyntaxHighlighter from "react-syntax-highlighter"; import SyntaxHighlighter from "react-syntax-highlighter";
import createElement from "react-syntax-highlighter/dist/esm/create-element"; import createElement from "react-syntax-highlighter/dist/esm/create-element";
import { atomOneDark } from "react-syntax-highlighter/dist/esm/styles/hljs"; import { atomOneDark } from "react-syntax-highlighter/dist/esm/styles/hljs";
import { cn } from "@/app/common/utils";
import { capitalizeFirstLetter } from "@/app/common/string-utils";
type CodeHighlighterProps = { type CodeHighlighterProps = {
/** /**
@ -69,18 +71,19 @@ function rowRenderer({
export function CodeHighlighter({ code, language = "json", rounded = true }: CodeHighlighterProps): ReactElement { export function CodeHighlighter({ code, language = "json", rounded = true }: CodeHighlighterProps): ReactElement {
return ( return (
<div className="text-xs md:text-md"> <div className="text-xs md:text-md relative">
{/* Language */}
<div className="absolute top-0 right-0 p-1 bg-muted rounded-bl-md rounded-tr-md">
<span className="text-xs text-muted-foreground">{capitalizeFirstLetter(language)}</span>
</div>
{/* Code */}
<SyntaxHighlighter <SyntaxHighlighter
className={cn("max-h-[600px] !bg-secondary break-all rounded-md", rounded && "rounded-md")}
language={language} language={language}
style={atomOneDark} style={atomOneDark}
wrapLongLines wrapLongLines
renderer={rowRenderer} renderer={rowRenderer}
customStyle={{
maxHeight: "600px",
backgroundColor: "hsl(var(--background-accent))",
wordBreak: "break-all",
borderRadius: rounded ? "0.75rem" : undefined,
}}
> >
{code} {code}
</SyntaxHighlighter> </SyntaxHighlighter>

View File

@ -0,0 +1,117 @@
"use client";
import React, { ReactElement, useState } from "react";
import { DocsContentMetadata } from "@/app/common/documentation";
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/app/components/ui/command";
import { Button, ButtonProps } from "@/app/components/ui/button";
import { useRouter } from "next/navigation";
import { cn } from "@/app/common/utils";
export function CommandMenu({ ...props }: ButtonProps): ReactElement {
const router = useRouter();
/**
* Whether to show the search
*/
const [open, setOpen] = useState<boolean>(false);
/**
* The pages that were found
*/
const [pages, setPages] = useState<DocsContentMetadata[] | undefined>(undefined);
// Handle keyboard shortcuts
React.useEffect(() => {
const down = (e: KeyboardEvent) => {
if ((e.key === "k" && (e.metaKey || e.ctrlKey)) || e.key === "/") {
if (
(e.target instanceof HTMLElement && e.target.isContentEditable) ||
e.target instanceof HTMLInputElement ||
e.target instanceof HTMLTextAreaElement ||
e.target instanceof HTMLSelectElement
) {
return;
}
e.preventDefault();
setOpen(open => !open);
}
};
document.addEventListener("keydown", down);
return () => document.removeEventListener("keydown", down);
}, []);
/**
* Search the documentation
* for the given query.
*
* @param query the query to search for
*/
async function searchDocs(query: string): Promise<void> {
// Don't bother searching if the query is less than 3 characters
if (query.length < 3) {
setPages(undefined);
return;
}
// Attempt to search for the query
const response = await fetch(`/api/docs/search?query=${query}`);
const pages: DocsContentMetadata[] = await response.json();
setPages(pages);
}
return (
<>
<Button
variant="outline"
className={cn(
"relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-64",
props.className,
)}
onClick={() => setOpen(true)}
>
Search
</Button>
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput
placeholder="Query..."
onValueChange={async search => {
await searchDocs(search);
}}
/>
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
{pages && pages.length > 1 && (
<CommandGroup heading="Suggestions">
{pages.map(page => {
return (
<CommandItem
key={page.slug}
onSelect={() => {
router.push(`/docs/${page.slug}`); // Go to the page
setOpen(false); // Close the dialog
setPages(undefined); // Clear the pages
}}
className="flex flex-col items-start gap-1"
>
<p className="font-semibold text-primary">{page.title}</p>
<p>{page.summary}</p>
</CommandItem>
);
})}
</CommandGroup>
)}
</CommandList>
</CommandDialog>
</>
);
}

View File

@ -10,7 +10,7 @@ type ContainerProps = {
export default function Container({ children }: ContainerProps): ReactElement { export default function Container({ children }: ContainerProps): ReactElement {
return ( return (
<div className="z-[9999] m-auto flex h-screen min-h-full flex-col items-center opacity-90 w-full xs:max-w-[1200px]"> <div className="z-[9999] m-auto flex h-screen flex-col items-center opacity-90 w-full xs:max-w-[1200px]">
<NavBar /> <NavBar />
<div className="w-full flex mt-4 justify-center h-full">{children}</div> <div className="w-full flex mt-4 justify-center h-full">{children}</div>
</div> </div>

View File

@ -10,6 +10,11 @@ type CopyButtonProps = {
*/ */
content: string; content: string;
/**
* The message to display when the content is copied.
*/
message?: string | boolean;
/** /**
* The children for this element. * The children for this element.
*/ */
@ -22,7 +27,7 @@ type CopyButtonProps = {
* @param props the properties for the button * @param props the properties for the button
* @returns the copy button * @returns the copy button
*/ */
export function CopyButton({ content, children }: CopyButtonProps): ReactElement { export function CopyButton({ content, message, children }: CopyButtonProps): ReactElement {
const { toast } = useToast(); const { toast } = useToast();
return ( return (
@ -33,7 +38,7 @@ export function CopyButton({ content, children }: CopyButtonProps): ReactElement
title: "Copied!", title: "Copied!",
description: ( description: (
<p> <p>
Copied <b>{content}</b> to your clipboard. Copied <b>{!message ? content : message}</b> to your clipboard.
</p> </p>
), ),
duration: 5000, duration: 5000,

View File

@ -0,0 +1,21 @@
"use client";
import { ReactElement } from "react";
import { Button } from "@/app/components/ui/button";
import { CreateHasteButtonProps } from "@/app/types/create-haste-button";
import { createHaste } from "@/app/common/hastebin";
export function CreateHasteButton({ content }: CreateHasteButtonProps): ReactElement {
/**
* Uploads the content to Haste and opens the URL in a new tab.
*/
async function upload(): Promise<void> {
const url = await createHaste(content);
console.log(url);
// Open the URL in a new tab.
window.open(url, "_blank", "noopener,noreferrer");
}
return <Button onClick={() => upload()}>Create Haste</Button>;
}

View File

@ -0,0 +1,48 @@
import { ReactElement } from "react";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
} from "@/app/components/ui/breadcrumb";
import { capitalizeFirstLetter } from "@/app/common/string-utils";
import { DocsContentMetadata } from "@/app/common/documentation";
type DocsBreadcrumbProps = {
/**
* The page to render the breadcrumb for.
*/
page: DocsContentMetadata;
};
export function DocsBreadcrumb({ page }: DocsBreadcrumbProps): ReactElement {
const slugParts: string[] = page.slug.split("/");
const isHome: boolean = slugParts.length == 1;
return (
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href={`/docs`}>Home</BreadcrumbLink>
</BreadcrumbItem>
{!isHome &&
slugParts.map((slug, index, array) => {
const path: string = array.slice(0, index + 1).join("/");
const name: string = slug.includes("-")
? slug.split("-").map(capitalizeFirstLetter).join(" ")
: capitalizeFirstLetter(slug);
return (
<div key={slug} className="flex items-center ">
<BreadcrumbSeparator className="pr-1.5" />
<BreadcrumbItem>
<BreadcrumbLink href={`/docs/${path}`}>{capitalizeFirstLetter(name)}</BreadcrumbLink>
</BreadcrumbItem>
</div>
);
})}
</BreadcrumbList>
</Breadcrumb>
);
}

View File

@ -1,40 +0,0 @@
"use client";
import { DocsContentMetadata } from "@/app/common/documentation";
import React, { ReactElement } from "react";
import { DialogClose } from "../ui/dialog";
import { useRouter } from "next/navigation";
type PagesProps = {
/**
* The documentation pages to display.
*/
pages: DocsContentMetadata[] | undefined;
};
export function DocumentationPages({ pages }: PagesProps): ReactElement {
const router = useRouter();
return (
<>
{pages && pages.length === 0 && <p>No results found</p>}
{pages &&
pages.length > 1 &&
pages.map(page => {
return (
<DialogClose
key={page.slug}
className="text-left bg-card p-2 rounded-lg"
onClick={() => {
router.replace(`/docs/${page.slug}`);
}}
>
<h2 className="font-semibold">{page.title}</h2>
<p className="text-accent">{page.summary}</p>
</DialogClose>
);
})}
</>
);
}

View File

@ -0,0 +1,22 @@
import { DocsContentMetadata } from "@/app/common/documentation";
import { ReactElement } from "react";
import Image from "next/image";
import Link from "next/link";
type GithubLink = {
/**
* The page to link to.
*/
page: DocsContentMetadata;
};
export function GithubLink({ page }: GithubLink): ReactElement {
return (
<Link
href={`https://git.fascinated.cc/MinecraftUtilities/Frontend/src/branch/master/documentation/${page.slug}.md`}
target="_blank"
>
<Image src="/media/github.png" alt="The GitHub logo" width={32} height={32} className="filter dark:invert" />
</Link>
);
}

View File

@ -1,55 +0,0 @@
"use client";
import React, { ReactElement, useState } from "react";
import { Button } from "@/app/components/ui/button";
import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/app/components/ui/dialog";
import { DocsContentMetadata } from "@/app/common/documentation";
import { DocumentationPages } from "@/app/components/docs/documentation-pages";
export function Search(): ReactElement {
/**
* The pages that were found
*/
const [pages, setPages] = useState<DocsContentMetadata[] | undefined>(undefined);
/**
* Search the documentation
* for the given query.
*
* @param query the query to search for
*/
async function searchDocs(query: string): Promise<void> {
// Don't bother searching if the query is less than 3 characters
if (query.length < 3) {
return setPages(undefined);
}
// Attempt to search for the query
const response = await fetch(`/api/docs/search?query=${query}`);
const pages: DocsContentMetadata[] = await response.json();
setPages(pages);
}
return (
<div className="w-full md:w-[250px] min-h-fit h-fit bg-card rounded-lg p-3">
<div className="flex flex-col w-full">
<Dialog>
<DialogTrigger asChild>
<Button>Search</Button>
</DialogTrigger>
<DialogContent className="w-[600px]">
<DialogTitle>Search Documentation</DialogTitle>
<input
type="text"
placeholder="Search documentation"
className="w-full p-2 border border-border rounded-lg focus:outline-none focus:ring-2 focus:border-ring"
onChange={e => searchDocs(e.target.value)}
/>
<DocumentationPages pages={pages} />
</DialogContent>
</Dialog>
</div>
</div>
);
}

View File

@ -3,8 +3,16 @@
import { ReactElement, useEffect, useState } from "react"; import { ReactElement, useEffect, useState } from "react";
import { Star } from "lucide-react"; import { Star } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { cn } from "@/app/common/utils";
export function GithubStar(): ReactElement { type GithubStarProps = {
/**
* The class name for this component.
*/
className?: string;
};
export function GithubStar({ className }: GithubStarProps): ReactElement {
const [starCount, setStarCount] = useState(0); const [starCount, setStarCount] = useState(0);
const getStarCount = async () => { const getStarCount = async () => {
@ -19,7 +27,10 @@ export function GithubStar(): ReactElement {
return ( return (
<Link <Link
className="bg-github-green px-2 py-1 rounded-lg items-center gap-1 hover:opacity-85 transform-gpu transition-all hidden md:flex" className={cn(
"bg-github-green px-2 py-1 rounded-lg items-center gap-1 hover:opacity-85 transform-gpu transition-all hidden md:flex",
className,
)}
href="https://github.com/RealFascinated/MinecraftUtilities" href="https://github.com/RealFascinated/MinecraftUtilities"
target="_blank" target="_blank"
> >

View File

@ -1,6 +1,15 @@
import { MDXRemote } from "remote-mdx/rsc"; import { MDXRemote } from "remote-mdx/rsc";
import { ReactElement } from "react"; import { ReactElement } from "react";
import { formatCode, formatHeading, formatLink, formatList } from "@/app/components/mdx-renderer"; import {
formatCode,
formatHeading,
formatLink,
formatList,
formatTable,
formatTableData,
formatTableHeader,
} from "@/app/components/mdx-renderer";
import remarkGfm from "remark-gfm";
/** /**
* The components to use in the MDX renderer. * The components to use in the MDX renderer.
@ -15,8 +24,21 @@ const components = {
code: (props: any) => formatCode(props), code: (props: any) => formatCode(props),
ul: (props: any) => formatList(props), ul: (props: any) => formatList(props),
a: (props: any) => formatLink(props), a: (props: any) => formatLink(props),
table: (props: any) => formatTable(props),
th: (props: any) => formatTableHeader(props),
td: (props: any) => formatTableData(props),
}; };
export function CustomMDX(props: any): ReactElement { export function CustomMDX(props: any): ReactElement {
return <MDXRemote {...props} components={{ ...components, ...(props.components || {}) }} />; return (
<MDXRemote
{...props}
components={{ ...components, ...(props.components || {}) }}
options={{
mdxOptions: {
remarkPlugins: [remarkGfm],
},
}}
/>
);
} }

View File

@ -32,12 +32,10 @@ export function formatCode(props: any): ReactElement {
return <code className="text-xs bg-secondary p-1 rounded-md leading-none" {...props} />; return <code className="text-xs bg-secondary p-1 rounded-md leading-none" {...props} />;
} }
const language = props.className.replace("language-", "");
return ( return (
<div className="pt-4"> <div className="pt-4">
<CodeHighlighter <CodeHighlighter language={props.className ? language : undefined} code={props.children} />
language={props.className ? props.className.replace("language-") : undefined}
code={props.children}
/>
</div> </div>
); );
} }
@ -63,3 +61,30 @@ export function formatLink(props: any): ReactElement {
</Link> </Link>
); );
} }
/**
* Format a table.
*
* @param props The props to pass to the table.
*/
export function formatTable(props: any): ReactElement {
return <table className="table-auto divide-y divide-gray-200 mt-4">{props.children}</table>;
}
/**
* Format table header.
*
* @param props The props to pass to the table header.
*/
export function formatTableHeader(props: any): ReactElement {
return <th className="border-border border p-1.5">{props.children}</th>;
}
/**
* Format table data.
*
* @param props The props to pass to the table data.
*/
export function formatTableData(props: any): ReactElement {
return <td className="border-border border p-1.5">{props.children}</td>;
}

View File

@ -9,6 +9,7 @@ import { ToggleThemeButton } from "./theme-toggle-button";
import { GithubStar } from "@/app/components/github-star"; import { GithubStar } from "@/app/components/github-star";
import { Card } from "@/app/components/card"; import { Card } from "@/app/components/card";
import { cn } from "@/app/common/utils"; import { cn } from "@/app/common/utils";
import { CommandMenu } from "@/app/components/command-menu";
type Page = { type Page = {
/** /**
@ -42,7 +43,8 @@ const pages: Page[] = [
]; ];
export default function NavBar(): ReactElement { export default function NavBar(): ReactElement {
const path = usePathname(); const path: string = usePathname();
const isDocs: boolean = path ? path.includes("/docs") : false;
return ( return (
<Card <Card
@ -50,18 +52,20 @@ export default function NavBar(): ReactElement {
classNameContent="p-0 relative rounded-lg flex justify-between items-center gap-3 px-3 bg-opacity-85 h-12" classNameContent="p-0 relative rounded-lg flex justify-between items-center gap-3 px-3 bg-opacity-85 h-12"
> >
{/* Left */} {/* Left */}
<div className="z-50"> <div className={cn("flex flex-row items-center gap-2 z-50", isDocs ? "w-full md:w-fit" : "w-fit")}>
<Link href="/" className="flex items-center gap-2"> <Link href="/" className="flex items-center gap-2">
<Logo /> <Logo />
<p className="hidden md:block text-lg font-semibold">Minecraft Utilities</p>
</Link> </Link>
{/* Command Menu */}
<CommandMenu className={cn(isDocs ? "" : "hidden md:inline-flex")} />
</div> </div>
{/* Links */} {/* Links */}
<div className="absolute inset-x-0 flex justify-center"> <div className={cn("absolute inset-x-0 justify-center", isDocs ? "hidden md:flex" : "flex")}>
<div className="flex gap-4"> <div className="flex gap-4">
{pages.map((page, index) => { {pages.map((page, index) => {
const isActive = path.includes(page.url); const isActive: boolean = path ? path.includes(page.url) : false;
return ( return (
<HrefButton <HrefButton
@ -79,7 +83,7 @@ export default function NavBar(): ReactElement {
{/* Right */} {/* Right */}
<div className="flex gap-4 items-center z-50"> <div className="flex gap-4 items-center z-50">
<ToggleThemeButton /> <ToggleThemeButton />
<GithubStar /> <GithubStar className={isDocs ? "hidden md:flex" : "hidden"} />
</div> </div>
</Card> </Card>
); );

View File

@ -8,6 +8,7 @@ import { Separator } from "../ui/separator";
import { SkinPartImage } from "./skin-part-image"; import { SkinPartImage } from "./skin-part-image";
import { CacheInformation } from "@/app/components/cache-information"; import { CacheInformation } from "@/app/components/cache-information";
import { PlayerSkin } from "@/app/components/player/player-skin"; import { PlayerSkin } from "@/app/components/player/player-skin";
import { ReloadPageButton } from "@/app/components/reload-page-button";
type PlayerViewProps = { type PlayerViewProps = {
/** /**
@ -38,6 +39,7 @@ export function PlayerView({ player }: PlayerViewProps): ReactElement {
</div> </div>
</Card> </Card>
<div className="flex gap-2 flex-wrap justify-center"> <div className="flex gap-2 flex-wrap justify-center">
<ReloadPageButton />
<CodeDialog <CodeDialog
title="Player Data" title="Player Data"
description="The player's data from the API" description="The player's data from the API"

View File

@ -0,0 +1,49 @@
import { ReactElement } from "react";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/app/components/ui/tooltip";
import Image from "next/image";
import config from "@root/config.json";
import Link from "next/link";
import { Card } from "@/app/components/card";
/**
* The players to try out.
*/
const tryMePlayers: string[] = ["Notch", "jeb_", "Dinnerbone", "Grumm", "deadmau5"];
export function TryAPlayer(): ReactElement {
return (
<Card>
<div className="flex flex-col items-center justify-center text-center">
<div>
<h2 className="text-lg font-semibold">Try a Player</h2>
<p className="text-muted-foreground">Try one of these players to see how the player view works.</p>
</div>
<div className="flex flex-wrap justify-center gap-2 max-w-2xl mt-4">
{tryMePlayers.map(playerName => (
<Tooltip key={playerName}>
<TooltipTrigger asChild>
<div className="flex items-center justify-center bg-background p-1.5 rounded-md gap-2">
<Image
src={`${config.apiEndpoint}/player/head/${playerName}`}
alt={"The player's head"}
width={32}
height={32}
/>
<Link href={`/player/${playerName}`} className="hover:opacity-85 transform-gpu transition-all">
{playerName}
</Link>
</div>
</TooltipTrigger>
<TooltipContent>
<p>
Click to try the player <b>{playerName}</b>.
</p>
</TooltipContent>
</Tooltip>
))}
</div>
</div>
</Card>
);
}

View File

@ -0,0 +1,28 @@
"use client";
import { ReactElement } from "react";
import { Button } from "@/app/components/ui/button";
import { ArrowPathIcon } from "@heroicons/react/16/solid";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/app/components/ui/tooltip";
export function ReloadPageButton(): ReactElement {
/**
* Reload the page.
*/
function reload(): void {
window.location.reload();
}
return (
<Tooltip>
<TooltipTrigger asChild>
<Button onClick={() => reload()}>
<ArrowPathIcon className="w-4 h-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Reload the page</p>
</TooltipContent>
</Tooltip>
);
}

View File

@ -4,6 +4,7 @@ import { ReactElement } from "react";
import { CodeDialog } from "../code-dialog"; import { CodeDialog } from "../code-dialog";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { CacheInformation } from "@/app/components/cache-information"; import { CacheInformation } from "@/app/components/cache-information";
import { ReloadPageButton } from "@/app/components/reload-page-button";
type ServerViewProps = { type ServerViewProps = {
/** /**
@ -30,6 +31,7 @@ export function ServerView({ server, favicon }: ServerViewProps): ReactElement {
/> />
</div> </div>
<div className="flex gap-2 flex-wrap justify-center"> <div className="flex gap-2 flex-wrap justify-center">
<ReloadPageButton />
<CodeDialog <CodeDialog
title="Server Data" title="Server Data"
description="The servers's data from the API" description="The servers's data from the API"

View File

@ -0,0 +1,54 @@
import { ReactElement } from "react";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/app/components/ui/tooltip";
import Image from "next/image";
import Link from "next/link";
import { capitalizeFirstLetter } from "@/app/common/string-utils";
import { Card } from "@/app/components/card";
import { ServerPlatform } from "mcutils-library";
import { TryMeServer } from "@/app/types/server/try-me-server";
/**
* The servers to try out.
*/
const tryMeServers: TryMeServer[] = [
{ platform: ServerPlatform.Java, hostname: "mc.hypixel.net" },
{ platform: ServerPlatform.Java, hostname: "wildprison.net" },
{ platform: ServerPlatform.Java, hostname: "cubecraft.net" },
{ platform: ServerPlatform.Bedrock, hostname: "geo.hivebedrock.network" },
];
export function TryAServer(): ReactElement {
return (
<Card>
<div className="flex flex-col items-center justify-center text-center">
<div>
<h2 className="text-lg font-semibold">Try a Server</h2>
<p className="text-muted-foreground">Try one of these servers to see how the server view works.</p>
</div>
<div className="flex flex-wrap justify-center gap-2 max-w-2xl mt-4">
{tryMeServers.map(({ platform, hostname }) => (
<Tooltip key={hostname}>
<TooltipTrigger asChild>
<div className="flex items-center justify-center bg-background p-1.5 rounded-md gap-2">
<Image src={`/media/platform/${platform}.png`} alt={"The server's platform"} width={28} height={28} />
<Link
href={`/server/${platform}/${hostname}`}
className="hover:opacity-85 transform-gpu transition-all"
>
{hostname}
</Link>
</div>
</TooltipTrigger>
<TooltipContent>
<p>
Click to try the <b>{capitalizeFirstLetter(platform)}</b> server: <b>{hostname}</b>
</p>
</TooltipContent>
</Tooltip>
))}
</div>
</div>
</Card>
);
}

View File

@ -0,0 +1,135 @@
"use client";
import * as React from "react";
import { type DialogProps } from "@radix-ui/react-dialog";
import { Command as CommandPrimitive } from "cmdk";
import { Search } from "lucide-react";
import { cn } from "@/app/common/utils";
import { Dialog, DialogContent } from "@/app/components/ui/dialog";
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
className,
)}
shouldFilter={false}
{...props}
/>
));
Command.displayName = CommandPrimitive.displayName;
interface CommandDialogProps extends DialogProps {}
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
return (
<Dialog {...props}>
<DialogContent className="overflow-hidden p-0 shadow-lg">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children}
</Command>
</DialogContent>
</Dialog>
);
};
const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
ref={ref}
className={cn(
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
/>
</div>
));
CommandInput.displayName = CommandPrimitive.Input.displayName;
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
{...props}
/>
));
CommandList.displayName = CommandPrimitive.List.displayName;
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => <CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} />);
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Group
ref={ref}
className={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
className,
)}
{...props}
/>
));
CommandGroup.displayName = CommandPrimitive.Group.displayName;
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator ref={ref} className={cn("-mx-1 h-px bg-border", className)} {...props} />
));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled='true']:pointer-events-none data-[disabled='true']:opacity-50",
className,
)}
{...props}
/>
));
CommandItem.displayName = CommandPrimitive.Item.displayName;
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)} {...props} />;
};
CommandShortcut.displayName = "CommandShortcut";
export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandShortcut,
CommandSeparator,
};

View File

@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content <DialogPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-md md:max-w-2xl xl:max-w-6xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", "fixed left-[50%] top-[50%] z-50 grid max-w-md md:max-w-2xl xl:max-w-6xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className, className,
)} )}
{...props} {...props}

22
src/app/global-error.tsx Normal file
View File

@ -0,0 +1,22 @@
"use client";
import * as Sentry from "@sentry/nextjs";
import Error from "next/error";
import { useEffect } from "react";
import { Button } from "@/app/components/ui/button";
export default function GlobalError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
useEffect(() => {
Sentry.captureException(error);
}, [error]);
return (
<div className="flex text-center flex-col gap-4">
<div>
<h2 className="text-red-400 font-2xl font-semibold">Error</h2>
<p>An error occurred while rendering this page.</p>
</div>
<Button onClick={reset}>Reload</Button>
</div>
);
}

View File

@ -2,51 +2,51 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@layer base { @layer base {
:root { :root {
--background: 0 0% 100%; --background: 0 0% 100%;
--foreground: 240 10% 3.9%; --foreground: 0 0% 3.9%;
--card: 0 0% 100%; --card: 0 0% 95%;
--card-foreground: 240 10% 3.9%; --card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%; --popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%; --popover-foreground: 0 0% 3.9%;
--primary: 221.2 83.2% 53.3%; --primary: 221.2 83.2% 53.3%;
--primary-foreground: 355.7 100% 97.3%; --primary-foreground: 0 0% 98%;
--secondary: 5 5% 95%; --secondary: 0 0% 96.1%;
--secondary-foreground: 240 5.9% 10%; --secondary-foreground: 0 0% 9%;
--muted: 240 4.8% 95.9%; --muted: 0 0% 96.1%;
--muted-foreground: 240 3.8% 46.1%; --muted-foreground: 0 0% 45.1%;
--accent: 240 4.8% 95.9%; --accent: 0 0% 96.1%;
--accent-foreground: 240 5.9% 10%; --accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%; --destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%; --destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%; --border: 0 0% 89.8%;
--input: 240 5.9% 90%; --input: 0 0% 89.8%;
--ring: 221.2 83.2% 53.3%; --ring: 0 0% 3.9%;
--radius: 0.5rem; --radius: 0.3rem;
} }
.dark { .dark {
--background: 20 14.3% 6.5%; --background: 0 0% 3.9%;
--background-accent: 20 14.3% 8.5%; --foreground: 0 0% 98%;
--foreground: 0 0% 95%; --card: 0 0% 8%;
--card: 24 9.8% 10%; --card-foreground: 0 0% 98%;
--card-foreground: 0 0% 95%; --popover: 0 0% 3.9%;
--popover: 0 0% 9%; --popover-foreground: 0 0% 98%;
--popover-foreground: 0 0% 95%;
--primary: 217.2 91.2% 59.8%; --primary: 217.2 91.2% 59.8%;
--primary-foreground: 144.9 80.4% 10%; --primary-foreground: 0 0% 9%;
--secondary: 240 3.7% 15.9%; --secondary: 0 0% 9%;
--secondary-foreground: 0 0% 98%; --secondary-foreground: 0 0% 98%;
--muted: 0 0% 15%; --muted: 0 0% 14.9%;
--muted-foreground: 240 5% 64.9%; --muted-foreground: 0 0% 63.9%;
--accent: 12 6.5% 75%; --accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%; --accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%; --destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%; --destructive-foreground: 0 0% 98%;
--border: 240 5.9% 30%; --border: 0 0% 14.9%;
--input: 240 3.7% 15.9%; --input: 0 0% 14.9%;
--ring: 224.3 76.3% 48%; --ring: 0 0% 83.1%;
} }
} }

View File

@ -5,10 +5,10 @@ import Container from "./components/container";
import ThemeProvider from "./components/theme-provider"; import ThemeProvider from "./components/theme-provider";
import { Toaster } from "./components/ui/toaster"; import { Toaster } from "./components/ui/toaster";
import { TooltipProvider } from "./components/ui/tooltip"; import { TooltipProvider } from "./components/ui/tooltip";
import { inter } from "@/app/font/fonts";
import config from "@root/config.json";
import "./globals.css"; import "./globals.css";
import config from "@root/config.json";
import { inter } from "@/app/font/fonts";
export const viewport: Viewport = { export const viewport: Viewport = {
themeColor: "#3498DB", themeColor: "#3498DB",

View File

@ -1,7 +1,8 @@
import Link from "next/link"; import Link from "next/link";
import { Button } from "@/app/components/ui/button"; import { Button } from "@/app/components/ui/button";
import { ReactElement } from "react";
export default function NotFound() { export default function NotFound(): ReactElement {
return ( return (
<div className="flex text-center flex-col gap-4"> <div className="flex text-center flex-col gap-4">
<div> <div>

View File

@ -0,0 +1,6 @@
export type CreateHasteButtonProps = {
/**
* The content to create the haste with.
*/
content: string;
};

View File

@ -0,0 +1,27 @@
export type LandingButton = {
/**
* The title of the button.
*/
title: string;
/**
* The tooltip to display for this statistic.
*/
tooltip: string;
/**
* The URL to go to.
*/
url: string;
/**
* Whether clicking the button will
* open the link in a new tab.
*/
openInNewTab?: boolean;
/**
* The class name to apply to the button.
*/
className?: string;
};

View File

@ -0,0 +1,5 @@
export type PlayerPageParams = {
params: {
id: string;
};
};

View File

@ -0,0 +1,8 @@
import { ServerPlatform } from "mcutils-library";
export type ServerPageParams = {
params: {
platform: ServerPlatform;
hostname: string;
};
};

View File

@ -0,0 +1,13 @@
import { ServerPlatform } from "mcutils-library";
export type TryMeServer = {
/**
* The platform of the server.
*/
platform: ServerPlatform;
/**
* The hostname of the server.
*/
hostname: string;
};

9
src/instrumentation.ts Normal file
View File

@ -0,0 +1,9 @@
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('../sentry.server.config');
}
if (process.env.NEXT_RUNTIME === 'edge') {
await import('../sentry.edge.config');
}
}