feat: revamped documentation

This commit is contained in:
dvelo 2024-11-03 09:38:25 -06:00
parent dd9104f2f3
commit 3c1fd6cfc4
38 changed files with 3731 additions and 3237 deletions

@ -11,12 +11,12 @@ The command-bar has many mods and is a great tool for power-users to use as its
## Triggering the command-bar ## Triggering the command-bar
There are two ways to trigger the command bar, using `Ctrl+K` and `Ctrl+Shift+K`. Both put you in a command-bar, however when using `Ctrl+K`, you go into a general page with other settings. There are two ways to trigger the command bar, using `Ctrl+K` and <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>K</kbd>. Both put you in a command-bar, however when using `Ctrl+K`, you go into a general page with other settings.
Using `Ctrl+Shift+K` opens a server viewer, and this may be faster then going through the general page. Using <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>K</kbd> opens a server viewer, and this may be faster then going through the general page.
## Functions using `Ctrl+K` ## Functions using Ctrl+K
- **Servers** opens a server list, same as `Ctrl+Shift+K` - **Servers** opens a server list, same as <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>K</kbd>
- **Sort Servers** allows you to go into a sorted server list quickly - **Sort Servers** allows you to go into a sorted server list quickly
- **Links** shows links useful for MHSF - **Links** shows links useful for MHSF
- **Pick Random Server** picks a random server and shows the user what that server is, is similar to the one on [the server list](/) - **Pick Random Server** picks a random server and shows the user what that server is, is similar to the one on [the server list](/)

@ -5,11 +5,11 @@ folder: "Guides"
# Owning a server # Owning a server
Owning a server is quite simple and allows you to [customize your server](/docs/guides/customization) your server and make it stand out from other servers. Before owning your server, make sure you agree to the [ECA](Docs:legal/external-content-agreement). Owning a server is quite simple and allows you to [customize your server](/docs/guides/customization) and make it stand out from other servers. Before owning your server, make sure you agree to the [ECA](Docs:legal/external-content-agreement).
## Linking ## Linking
Find the server you would like to own (either by looking for it, or using the keyboard shortcut `Ctrl`+`Shift`+`K` and searching for it), and make sure your account has [already been linked with your Minecraft account](Docs:guides/linking). Go to the server, and hit the Customization tab. If the owner of the server, and the user your linked to match, you will gain access to the server. Find the server you would like to own (either by looking for it, or using the keyboard shortcut <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>K</kbd> and searching for it), and make sure your account has [already been linked with your Minecraft account](Docs:guides/linking). Go to the server, and hit the Customization tab. If the owner of the server, and the user your linked to match, you will gain access to the server.
If they match, you should see a button named Click to own. Press that button, and you should automagically own the server. Congratulations! If they match, you should see a button named Click to own. Press that button, and you should automagically own the server. Congratulations!
## I can't link my server, because my server doesn't have a author ## I can't link my server, because my server doesn't have a author

@ -19,6 +19,8 @@
"@emotion/is-prop-valid": "^1.3.0", "@emotion/is-prop-valid": "^1.3.0",
"@linear/sdk": "^31.0.0", "@linear/sdk": "^31.0.0",
"@monaco-editor/react": "^4.6.0", "@monaco-editor/react": "^4.6.0",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-hover-card": "^1.1.1", "@radix-ui/react-hover-card": "^1.1.1",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-menubar": "^1.1.1", "@radix-ui/react-menubar": "^1.1.1",
@ -35,7 +37,7 @@
"inngest": "^3.21.2", "inngest": "^3.21.2",
"input-otp": "^1.2.4", "input-otp": "^1.2.4",
"json-beautify": "^1.1.1", "json-beautify": "^1.1.1",
"lucide-react": "^0.416.0", "lucide-react": "^0.454.0",
"minimessage-2-html": "1.6.0", "minimessage-2-html": "1.6.0",
"mongodb": "^6.8.0", "mongodb": "^6.8.0",
"next": "14.2.10", "next": "14.2.10",
@ -63,17 +65,17 @@
"@hookform/resolvers": "^3.9.0", "@hookform/resolvers": "^3.9.0",
"@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-context-menu": "^2.1.5", "@radix-ui/react-context-menu": "^2.1.5",
"@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-navigation-menu": "^1.1.4", "@radix-ui/react-navigation-menu": "^1.1.4",
"@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-radio-group": "^1.2.0",
"@radix-ui/react-scroll-area": "^1.1.0", "@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-tooltip": "^1.0.7", "@radix-ui/react-tooltip": "^1.1.3",
"@tailwindcss/typography": "^0.5.13", "@tailwindcss/typography": "^0.5.13",
"@types/canvas-confetti": "^1.6.4", "@types/canvas-confetti": "^1.6.4",
"@types/node": "^20", "@types/node": "^20",

@ -0,0 +1,129 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
import TableOfContent from "@/components/docs/TOC";
import { ALegacy } from "@/components/misc/Link";
import { MDXElements } from "@/components/misc/MDXElements";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import { allDocs } from "contentlayer/generated";
import { useMDXComponent } from "next-contentlayer/hooks";
import NextLink from "next/link";
import { notFound } from "next/navigation";
export const generateStaticParams = async () =>
allDocs.map((post) => ({ slug: [post._raw.flattenedPath] }));
export const generateMetadata = ({
params,
}: {
params: { slug: string[] };
}) => {
const post = allDocs.find(
(post) => post._raw.flattenedPath === params.slug.join("/"),
);
if (!post) notFound();
return { title: post.title + " | MHSF Docs", themeColor: "#000000" };
};
const PostLayout = ({ params }: { params: { slug: string[] } }) => {
const doc = allDocs.find(
(post) => post._raw.flattenedPath === params.slug.join("/"),
);
if (!doc) notFound();
console.log(doc);
const MDXContent = useMDXComponent(doc.body.code);
return (
<main className="relative py-6 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_300px]">
<div className="mx-auto w-full min-w-0">
<div className="pb-12 pt-8 prose dark:prose-invert">
{doc.folder && <span>{doc.folder}</span>}{" "}
{doc.lastUpdated && <span> - last updated {doc.lastUpdated}</span>}{" "}
<MDXContent
components={{
Separator,
a: (props) => <ALegacy {...props} />,
...MDXElements,
}}
/>
</div>
</div>
{doc.toc && (
<div className="hidden text-sm xl:block">
<div className="sticky top-16 -mt-10 pt-4">
<ScrollArea className="pb-10">
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] py-12 space-y-2">
<p className="font-medium">On This Page</p>
{doc.toc.map(
(c: { level: number; text: string; slug: string }) => (
<TableOfContent toc={c} doc={doc} key={c.slug} />
),
)}
<br />
<div className="space-y-2">
<p className="font-medium">Contribute</p>
<ul className="m-0 list-none">
<li className="mt-0 pt-2">
<NextLink
href={
"https://github.com/DeveloLongScript/MHSF/edit/main/docs/" +
doc._raw.flattenedPath +
".mdx"
}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground transition-colors no-underline"
>
<svg
viewBox="0 0 438.549 438.549"
fontSize={16}
className="mr-2 size-4"
>
<path
fill="currentColor"
d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"
/>
</svg>
Edit page on GitHub
</NextLink>
</li>
</ul>
</div>
</div>
</ScrollArea>
</div>
</div>
)}
</main>
);
};
export default PostLayout;

@ -28,47 +28,75 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
"use client";
import { Sidebar } from "@/components/docs/Sidebar"; import { Sidebar } from "@/components/docs/Sidebar";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"; import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer";
import { ScrollArea } from "@/components/ui/scroll-area"; import { ScrollArea } from "@/components/ui/scroll-area";
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar";
import { version } from "@/config/version"; import { version } from "@/config/version";
import { HamburgerMenuIcon } from "@radix-ui/react-icons"; import { HamburgerMenuIcon } from "@radix-ui/react-icons";
import { GeistMono } from "geist/font/mono";
import { GeistSans } from "geist/font/sans";
import "../globals.css";
import "../../themes.css";
import { ThemeProvider } from "@/components/ThemeProvider";
import { ClerkThemeProvider } from "@/components/clerk/ClerkThemeProvider";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { Separator } from "@/components/ui/separator";
import NextTopLoader from "@/lib/top-loader";
import { useRouter } from "@/lib/useRouter";
import { allDocs } from "contentlayer/generated";
import { GetServerSideProps } from "next";
import { usePathname } from "next/navigation";
interface Props {
pathname: string;
}
export default async function RootLayout({ export default async function RootLayout({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
const pathname = usePathname();
return ( return (
<div className="border-b pt-[40px]"> <ClerkThemeProvider className="">
<div className="container flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10"> <div className="theme-zinc">
<aside className="fixed top-14 z-30 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block"> <NextTopLoader />
<ScrollArea className="h-full py-6 pr-6 lg:py-8"> <SidebarProvider>
<div className="bg-muted w-full rounded justify-center p-4 flex items-center">
MHSF Docs <small className="ml-2">Version {version}</small>
</div>
<br />
<Sidebar /> <Sidebar />
</ScrollArea> <SidebarInset>
</aside> <div className="fixed backdrop-blur w-full flex h-16 z-10 items-center gap-2 px-4 ">
<br className="md:hidden" /> <SidebarTrigger />
<Separator orientation="vertical" className="mr-2 h-4" />
<div className="bg-muted w-full rounded justify-center p-4 flex items-center md:hidden"> {
MHSF Docs <small className="ml-2">Version {version}</small> allDocs.find(
<Drawer> (c) =>
<DrawerTrigger> c._raw.flattenedPath ===
<Button className="ml-2"> pathname
<HamburgerMenuIcon /> ?.split("/")
</Button> .splice(2, pathname?.split("/").length)
</DrawerTrigger> .join("/"),
<DrawerContent className="p-4"> )?.title
<Sidebar /> }
</DrawerContent>
</Drawer>
</div>
{children}
</div> </div>
<div className="px-[100px] pt-[50px]">{children}</div>
</SidebarInset>
</SidebarProvider>
</div> </div>
</ClerkThemeProvider>
); );
} }

@ -28,36 +28,36 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { GeistSans } from "geist/font/sans";
import { SpeedInsights } from "@vercel/speed-insights/next";
import { Analytics } from "@vercel/analytics/react"; import { Analytics } from "@vercel/analytics/react";
import "./globals.css"; import { SpeedInsights } from "@vercel/speed-insights/next";
import { TooltipProvider } from "@/components/ui/tooltip"; import { GeistSans } from "geist/font/sans";
import "../globals.css";
import ClientFadeIn from "@/components/ClientFadeIn";
import { CommandBarer } from "@/components/CommandBar";
import { BrandingGenericIcon } from "@/components/Icon";
import TextFromPathname from "@/components/TextFromPathname";
import { ThemeProvider } from "@/components/ThemeProvider"; import { ThemeProvider } from "@/components/ThemeProvider";
import { ClerkThemeProvider } from "@/components/clerk/ClerkThemeProvider"; import { ClerkThemeProvider } from "@/components/clerk/ClerkThemeProvider";
import NextTopLoader from "@/lib/top-loader"; import TopBar from "@/components/clerk/Topbar";
import { banner } from "@/config/banner"; import ThemedToaster from "@/components/misc/ThemedToaster";
import UnofficalDialog from "@/components/misc/UnofficalDialog";
import { import {
Breadcrumb, Breadcrumb,
BreadcrumbList, BreadcrumbList,
BreadcrumbPage, BreadcrumbPage,
} from "@/components/ui/breadcrumb"; } from "@/components/ui/breadcrumb";
import Link from "next/link"; import { TooltipProvider } from "@/components/ui/tooltip";
import TopBar from "@/components/clerk/Topbar"; import { banner } from "@/config/banner";
import TextFromPathname from "@/components/TextFromPathname"; import NextTopLoader from "@/lib/top-loader";
import { Inter as interFont } from "next/font/google";
import { CommandBarer } from "@/components/CommandBar";
import ThemedToaster from "@/components/misc/ThemedToaster";
import UnofficalDialog from "@/components/misc/UnofficalDialog";
import ClientFadeIn from "@/components/ClientFadeIn";
import { BrandingGenericIcon } from "@/components/Icon";
import type { Metadata, Viewport } from "next"; import type { Metadata, Viewport } from "next";
import { Inter as interFont } from "next/font/google";
import Link from "next/link";
export const extraMetadata = { export const extraMetadata = {
twitter: { twitter: {
images: [ images: [
{ {
url: "/public/imgs/icon-cf.png", url: "/imgs/icon-cf.png",
}, },
], ],
}, },
@ -65,7 +65,7 @@ export const extraMetadata = {
openGraph: { openGraph: {
images: [ images: [
{ {
url: "/public/imgs/icon-cf.png", url: "/imgs/icon-cf.png",
}, },
], ],
}, },

@ -38,7 +38,7 @@ export default function NotFound() {
<br /> <br />
<p> <p>
We couldn't find the page you were looking for.{" "} We couldn't find the page you were looking for.{" "}
<Link href="/">Go home</Link> <Link href="/public">Go home</Link>
</p> </p>
</div> </div>
</main> </main>

@ -40,7 +40,7 @@ export const metadata: Metadata = {
twitter: { twitter: {
images: [ images: [
{ {
url: "/public/imgs/icon-cf.png", url: "/imgs/icon-cf.png",
}, },
], ],
}, },
@ -48,7 +48,7 @@ export const metadata: Metadata = {
openGraph: { openGraph: {
images: [ images: [
{ {
url: "/public/imgs/icon-cf.png", url: "/imgs/icon-cf.png",
}, },
], ],
}, },

@ -0,0 +1,155 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
import AfterServerView from "@/components/AfterServerView";
import Banner from "@/components/Banner";
import ColorProvider from "@/components/ColorProvider";
import ServerView from "@/components/ServerView";
import TabServer from "@/components/misc/TabServer";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { CornerDownLeft } from "lucide-react";
import type { Metadata, ResolvingMetadata } from "next";
import Link from "next/link";
type Props = {
params: { server: string };
};
export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata,
): Promise<Metadata> {
// read route params
const { server } = params;
const json = await (
await fetch("https://api.minehut.com/server/" + server + "?byName=true")
).json();
return {
themeColor: "#000000",
title:
json.server == null
? "Server doesn't exist | MHSF"
: json.server.name +
", " +
(json.server.online
? json.server.playerCount +
(json.server.maxPlayers != 10
? "/" + json.server.maxPlayers
: "") +
" online"
: "Offline") +
" | MHSF",
description:
json.server == null
? `The server ${server} doesn't exist.`
: `View ${server} on Minehut Server Finder!`,
authors: json.server == null ? undefined : { name: json.server.owner },
applicationName: "MHSF (Minehut Server Finder)",
icons:
json.server == null
? undefined
: "https://minehut-server-icons-live.s3.us-west-2.amazonaws.com/" +
(json.server.icon == undefined ? "OAK_SIGN" : json.server.icon) +
".png",
twitter: {
title:
json.server == null
? "Server doesn't exist | MHSF"
: json.server.name +
", " +
(json.server.online
? json.server.playerCount +
(json.server.maxPlayers != 10
? "/" + json.server.maxPlayers
: "") +
" online"
: "Offline") +
" | MHSF",
description:
json.server == null
? `The server ${server} doesn't exist.`
: `View ${server} on Minehut Server Finder!`,
images: [
{
url:
"https://minehut-server-icons-live.s3.us-west-2.amazonaws.com/" +
json.server.icon +
".png",
},
{
url: "/public/imgs/icon-cf.png",
},
],
},
openGraph: {
type: "profile",
siteName: "MHSF (Minehut Server Finder)",
images: [
{
url:
"https://minehut-server-icons-live.s3.us-west-2.amazonaws.com/" +
json.server.icon +
".png",
},
{
url: "/public/imgs/icon-cf.png",
},
],
},
};
}
export default function ServerPage({ params }: { params: { server: string } }) {
return (
<main>
<ColorProvider server={params.server}>
<div className={"pt-16"}>
<Banner server={params.server} />
<Link href="/">
<Button variant="link" className="text-muted-foreground text-sm">
<CornerDownLeft size={16} className="mr-2" /> Go back to the
server list
</Button>
</Link>
<TabServer server={params.server} tabDef="general" />
<div className="pt-8">
<ServerView server={params.server} />
</div>
<Separator />
<br />
<AfterServerView server={params.server} />
</div>
</ColorProvider>
</main>
);
}

@ -0,0 +1,116 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
import Banner from "@/components/Banner";
import ColorProvider from "@/components/ColorProvider";
import { NewChart } from "@/components/NewChart";
import ServerView from "@/components/ServerView";
import TabServer from "@/components/misc/TabServer";
import { Separator } from "@/components/ui/separator";
import type { Metadata, ResolvingMetadata } from "next";
type Props = {
params: { server: string };
};
export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata,
): Promise<Metadata> {
// read route params
const { server } = params;
const json = await (
await fetch("https://api.minehut.com/server/" + server + "?byName=true")
).json();
return {
title:
json.server == null
? "Server doesn't exist | MHSF"
: json.server.name +
", " +
(json.server.online
? json.server.playerCount +
(json.server.maxPlayers != 10
? "/" + json.server.maxPlayers
: "") +
" online"
: "Offline") +
" | MHSF",
description:
json.server == null
? `The server ${server} doesn't exist.`
: `View ${server} on Minehut Server Finder!`,
authors: json.server == null ? undefined : { name: json.server.owner },
applicationName: "MHSF (Minehut Server Finder)",
icons:
json.server == null
? undefined
: "https://mcapi.marveldc.me/item/" +
(json.server.icon == undefined ? "OAK_SIGN" : json.server.icon) +
"?width=64&height=64",
openGraph: {
type: "profile",
siteName: "MHSF (Minehut Server Finder)",
images: [
{
url:
"https://mcapi.marveldc.me/item/" +
json.server.icon +
"?width=64&height=64",
},
{
url: "/favicon.ico",
},
],
},
};
}
export default function ServerPage({ params }: { params: { server: string } }) {
return (
<main>
<ColorProvider server={params.server}>
<div className={"pt-16"}>
<Banner server={params.server} />
<TabServer server={params.server} tabDef="statistics" />
<div className="pt-8">
<ServerView server={params.server} />
<Separator />
<br />
<div className="p-4 gap-4">
<NewChart server={params.server} />
</div>
</div>
</div>
</ColorProvider>
</main>
);
}

@ -1,129 +0,0 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
import TableOfContent from "@/components/docs/TOC";
import { ScrollArea } from "@/components/ui/scroll-area";
import { allDocs } from "contentlayer/generated";
import { useMDXComponent } from "next-contentlayer/hooks";
import NextLink from "next/link";
import { notFound } from "next/navigation";
import { Separator } from "@/components/ui/separator";
import { ALegacy } from "@/components/misc/Link";
import { MDXElements } from "@/components/misc/MDXElements";
export const generateStaticParams = async () =>
allDocs.map((post) => ({ slug: [post._raw.flattenedPath] }));
export const generateMetadata = ({
params,
}: {
params: { slug: string[] };
}) => {
const post = allDocs.find(
(post) => post._raw.flattenedPath === params.slug.join("/")
);
if (!post) notFound();
return { title: post.title + " | MHSF Docs", themeColor: "#000000" };
};
const PostLayout = ({ params }: { params: { slug: string[] } }) => {
const doc = allDocs.find(
(post) => post._raw.flattenedPath === params.slug.join("/")
);
if (!doc) notFound();
console.log(doc);
const MDXContent = useMDXComponent(doc.body.code);
return (
<main className="relative py-6 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_300px]">
<div className="mx-auto w-full min-w-0">
<div className="pb-12 pt-8 prose dark:prose-invert">
{doc.folder && <span>{doc.folder}</span>}{" "}
{doc.lastUpdated && <span> - last updated {doc.lastUpdated}</span>}{" "}
<MDXContent
components={{
Separator,
a: (props) => <ALegacy {...props} />,
...MDXElements,
}}
/>
</div>
</div>
{doc.toc && (
<div className="hidden text-sm xl:block">
<div className="sticky top-16 -mt-10 pt-4">
<ScrollArea className="pb-10">
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] py-12 space-y-2">
<p className="font-medium">On This Page</p>
{doc.toc.map(
(c: { level: number; text: string; slug: string }) => (
<TableOfContent toc={c} doc={doc} key={c.slug} />
)
)}
<br />
<div className="space-y-2">
<p className="font-medium">Contribute</p>
<ul className="m-0 list-none">
<li className="mt-0 pt-2">
<NextLink
href={
"https://github.com/DeveloLongScript/MHSF/edit/main/docs/" +
doc._raw.flattenedPath +
".mdx"
}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground transition-colors no-underline"
>
<svg
viewBox="0 0 438.549 438.549"
fontSize={16}
className="mr-2 size-4"
>
<path
fill="currentColor"
d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"
/>
</svg>
Edit page on GitHub
</NextLink>
</li>
</ul>
</div>
</div>
</ScrollArea>
</div>
</div>
)}
</main>
);
};
export default PostLayout;

@ -73,6 +73,15 @@
--color-one: #37ecba; --color-one: #37ecba;
--color-two: #72afd3; --color-two: #72afd3;
--color-three: #ff2e63; --color-three: #ff2e63;
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-mhsf: 240 1% 92%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
} }
.dark { .dark {
@ -112,6 +121,15 @@
--chart-3: 216 92% 60%; --chart-3: 216 92% 60%;
--chart-4: 210 98% 78%; --chart-4: 210 98% 78%;
--chart-5: 212 97% 87%; --chart-5: 212 97% 87%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-mhsf: 240 0% 13%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
} }
} }
@ -123,11 +141,11 @@
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
} }
@layer base { /*@layer base {*/
a { /* a {*/
@apply underline text-blue-400; /* @apply underline text-blue-400;*/
} /* }*/
} /*}*/
.backdrop-blur { .backdrop-blur {
-webkit-backdrop-filter: blur(8px) !important; -webkit-backdrop-filter: blur(8px) !important;

@ -1,152 +0,0 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
import ServerView from "@/components/ServerView";
import type { Metadata, ResolvingMetadata } from "next";
import TabServer from "@/components/misc/TabServer";
import ColorProvider from "@/components/ColorProvider";
import AfterServerView from "@/components/AfterServerView";
import Banner from "@/components/Banner";
import { Button } from "@/components/ui/button";
import { CornerDownLeft } from "lucide-react";
import Link from "next/link";
type Props = {
params: { server: string };
};
export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const { server } = params;
const json = await (
await fetch("https://api.minehut.com/server/" + server + "?byName=true")
).json();
return {
themeColor: "#000000",
title:
json.server == null
? "Server doesn't exist | MHSF"
: json.server.name +
", " +
(json.server.online
? json.server.playerCount +
(json.server.maxPlayers != 10
? "/" + json.server.maxPlayers
: "") +
" online"
: "Offline") +
" | MHSF",
description:
json.server == null
? `The server ${server} doesn't exist.`
: `View ${server} on Minehut Server Finder!`,
authors: json.server == null ? undefined : { name: json.server.owner },
applicationName: "MHSF (Minehut Server Finder)",
icons:
json.server == null
? undefined
: "https://minehut-server-icons-live.s3.us-west-2.amazonaws.com/" +
(json.server.icon == undefined ? "OAK_SIGN" : json.server.icon) +
".png",
twitter: {
title:
json.server == null
? "Server doesn't exist | MHSF"
: json.server.name +
", " +
(json.server.online
? json.server.playerCount +
(json.server.maxPlayers != 10
? "/" + json.server.maxPlayers
: "") +
" online"
: "Offline") +
" | MHSF",
description:
json.server == null
? `The server ${server} doesn't exist.`
: `View ${server} on Minehut Server Finder!`,
images: [
{
url:
"https://minehut-server-icons-live.s3.us-west-2.amazonaws.com/" +
json.server.icon +
".png",
},
{
url: "/public/imgs/icon-cf.png",
},
],
},
openGraph: {
type: "profile",
siteName: "MHSF (Minehut Server Finder)",
images: [
{
url:
"https://minehut-server-icons-live.s3.us-west-2.amazonaws.com/" +
json.server.icon +
".png",
},
{
url: "/public/imgs/icon-cf.png",
},
],
},
};
}
export default function ServerPage({ params }: { params: { server: string } }) {
return (
<main>
<ColorProvider server={params.server}>
<div className={"pt-16"}>
<Banner server={params.server} />
<Link href="/">
<Button variant="link" className="text-muted-foreground text-sm">
<CornerDownLeft size={16} className="mr-2" /> Go back to the
server list
</Button>
</Link>
<TabServer server={params.server} tabDef="general" />
<div className="pt-8">
<ServerView server={params.server} />
</div>
<AfterServerView server={params.server} />
</div>
</ColorProvider>
</main>
);
}

@ -1,113 +0,0 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
import ServerView from "@/components/ServerView";
import type { Metadata, ResolvingMetadata } from "next";
import TabServer from "@/components/misc/TabServer";
import { NewChart } from "@/components/NewChart";
import ColorProvider from "@/components/ColorProvider";
import Banner from "@/components/Banner";
type Props = {
params: { server: string };
};
export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const { server } = params;
const json = await (
await fetch("https://api.minehut.com/server/" + server + "?byName=true")
).json();
return {
title:
json.server == null
? "Server doesn't exist | MHSF"
: json.server.name +
", " +
(json.server.online
? json.server.playerCount +
(json.server.maxPlayers != 10
? "/" + json.server.maxPlayers
: "") +
" online"
: "Offline") +
" | MHSF",
description:
json.server == null
? `The server ${server} doesn't exist.`
: `View ${server} on Minehut Server Finder!`,
authors: json.server == null ? undefined : { name: json.server.owner },
applicationName: "MHSF (Minehut Server Finder)",
icons:
json.server == null
? undefined
: "https://mcapi.marveldc.me/item/" +
(json.server.icon == undefined ? "OAK_SIGN" : json.server.icon) +
"?width=64&height=64",
openGraph: {
type: "profile",
siteName: "MHSF (Minehut Server Finder)",
images: [
{
url:
"https://mcapi.marveldc.me/item/" +
json.server.icon +
"?width=64&height=64",
},
{
url: "/favicon.ico",
},
],
},
};
}
export default function ServerPage({ params }: { params: { server: string } }) {
return (
<main>
<ColorProvider server={params.server}>
<div className={"pt-16"}>
<Banner server={params.server} />
<TabServer server={params.server} tabDef="statistics" />
<div className="pt-8">
<ServerView server={params.server} />
<div className="p-4 gap-4">
<NewChart server={params.server} />
</div>
</div>
</div>
</ColorProvider>
</main>
);
}

@ -30,7 +30,17 @@
"use client"; "use client";
import { getCommunityServerFavorites, getCustomization } from "@/lib/api"; import { getCommunityServerFavorites, getCustomization } from "@/lib/api";
import { MHSF } from "@/lib/mhsf";
import { ServerResponse } from "@/lib/types/mh-server";
import { MinehutIcon, getMinehutIcons } from "@/lib/types/server-icon";
import { Copy, Info } from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import FadeIn from "react-fade-in/lib/FadeIn";
import toast, { CheckmarkIcon } from "react-hot-toast";
import Markdown from "react-markdown";
import AchievementList from "./feat/AchievementList";
import { Button } from "./ui/button";
import { import {
Card, Card,
CardContent, CardContent,
@ -38,28 +48,20 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "./ui/card"; } from "./ui/card";
import Markdown from "react-markdown";
import { useTheme } from "next-themes";
import FadeIn from "react-fade-in/lib/FadeIn";
import { Button } from "./ui/button";
import { ServerResponse } from "@/lib/types/mh-server";
import { Copy, Info } from "lucide-react";
import toast, { CheckmarkIcon } from "react-hot-toast";
import { MHSF } from "@/lib/mhsf";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
import AchievementList from "./feat/AchievementList";
export default function AfterServerView({ server }: { server: string }) { export default function AfterServerView({ server }: { server: string }) {
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [discord, setDiscord] = useState(""); const [discord, setDiscord] = useState("");
const [mhsf, setMHSF] = useState(new MHSF()); const [mhsf, setMHSF] = useState(new MHSF());
const [icons, setIcons] = useState<MinehutIcon[]>();
const { resolvedTheme } = useTheme(); const { resolvedTheme } = useTheme();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [view, setView] = useState( const [view, setView] = useState(
description !== "" || discord !== "" ? "desc" : "extra" description !== "" || discord !== "" ? "desc" : "extra",
); );
const [serverObject, setServerObject] = useState<ServerResponse | undefined>( const [serverObject, setServerObject] = useState<ServerResponse | undefined>(
undefined undefined,
); );
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
@ -72,9 +74,12 @@ export default function AfterServerView({ server }: { server: string }) {
getCommunityServerFavorites(server).then((c) => { getCommunityServerFavorites(server).then((c) => {
mhsf.setFavorites(c); mhsf.setFavorites(c);
}); });
getMinehutIcons().then((i) => {
setIcons(i);
});
} }
fetch("https://api.minehut.com/server/" + server + "?byName=true").then( fetch("https://api.minehut.com/server/" + server + "?byName=true").then(
(c) => c.json().then((n) => setServerObject(n.server)) (c) => c.json().then((n) => setServerObject(n.server)),
); );
setLoading(false); setLoading(false);
}); });
@ -108,6 +113,12 @@ export default function AfterServerView({ server }: { server: string }) {
> >
Achievements Achievements
</Button> </Button>
<Button
variant={view == "icons" ? undefined : "ghost"}
onClick={() => setView("icons")}
>
Purchased Icons
</Button>
</div> </div>
<div className="max-sm:hidden"> <div className="max-sm:hidden">
<div className="grid"> <div className="grid">
@ -131,6 +142,12 @@ export default function AfterServerView({ server }: { server: string }) {
> >
Achievements Achievements
</Button> </Button>
<Button
variant={view == "icons" ? undefined : "ghost"}
onClick={() => setView("icons")}
>
Purchased Icons
</Button>
</div> </div>
</div> </div>
@ -317,11 +334,11 @@ export default function AfterServerView({ server }: { server: string }) {
JSON.stringify({ JSON.stringify({
minehut: serverObject, minehut: serverObject,
mhsf: mhsf.getMHSF(), mhsf: mhsf.getMHSF(),
}) }),
); );
} catch { } catch {
toast.error( toast.error(
"Clipboard is inaccessible. Cannot copy" "Clipboard is inaccessible. Cannot copy",
); );
} }
toast.success( toast.success(
@ -334,7 +351,7 @@ export default function AfterServerView({ server }: { server: string }) {
}).substring(0, 36)} }).substring(0, 36)}
... ...
</code> </code>
</div> </div>,
); );
setTimeout(() => setCopied(false), 1000); setTimeout(() => setCopied(false), 1000);
}} }}
@ -393,6 +410,20 @@ export default function AfterServerView({ server }: { server: string }) {
</Card> </Card>
</div> </div>
)} )}
{view == "icons" && (
<div>
<p>
Purchased Icons are icons that are under the server's
ownership, they may or may not available at that certain
moment either.
</p>
{serverObject?.purchased_icons.map((icon) => (
<div key={icon}>
{icons?.find((c) => c._id === icon)?.icon_name}
</div>
))}
</div>
)}
</div> </div>
</div> </div>
<br /> <br />

@ -29,10 +29,11 @@
*/ */
"use client"; "use client";
import { Separator } from "@/components/ui/separator";
import { useState } from "react"; import { useState } from "react";
import TabServer from "./misc/TabServer";
import ServerCustomize from "./ServerCustomize";
import Banner from "./Banner"; import Banner from "./Banner";
import ServerCustomize from "./ServerCustomize";
import TabServer from "./misc/TabServer";
export default function CustomizeRoot({ export default function CustomizeRoot({
params, params,
@ -44,6 +45,7 @@ export default function CustomizeRoot({
<div className={"pt-16 theme-" + color}> <div className={"pt-16 theme-" + color}>
<Banner server={params.server} /> <Banner server={params.server} />
<TabServer server={params.server} tabDef="customize" /> <TabServer server={params.server} tabDef="customize" />
<Separator />
<br /> <br />
<div className="pl-[40px] pr-[40px]"> <div className="pl-[40px] pr-[40px]">
<ServerCustomize server={params.server} cs={color} setCS={setColor} /> <ServerCustomize server={params.server} cs={color} setCS={setColor} />

@ -28,15 +28,15 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { useClerk } from "@clerk/nextjs";
import { Cog, ExternalLink, KeyRound, Link, UserPen } from "lucide-react"; import { Cog, ExternalLink, KeyRound, Link, UserPen } from "lucide-react";
import NextLink from "next/link";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { import {
ResizableHandle, ResizableHandle,
ResizablePanel, ResizablePanel,
ResizablePanelGroup, ResizablePanelGroup,
} from "./ui/resizable"; } from "./ui/resizable";
import NextLink from "next/link";
import { useClerk } from "@clerk/nextjs";
export function Sidebar({ export function Sidebar({
children, children,
@ -54,7 +54,10 @@ export function Sidebar({
> >
<ResizablePanel className="max-md:hidden min-w-[285px] max-w-[285px] w-[285px]"> <ResizablePanel className="max-md:hidden min-w-[285px] max-w-[285px] w-[285px]">
<div className="w-[300px] ml-[10px]"> <div className="w-[300px] ml-[10px]">
<NextLink href="/account/settings" className="text-inherit"> <NextLink
href="/src/app/(main)/account/settings"
className="text-inherit"
>
<Button <Button
className="mb-[2px] w-[250px]" className="mb-[2px] w-[250px]"
variant={curPage !== "/account/settings" ? "ghost" : "default"} variant={curPage !== "/account/settings" ? "ghost" : "default"}
@ -62,7 +65,10 @@ export function Sidebar({
<Link size={16} className="mr-2" /> Linking <Link size={16} className="mr-2" /> Linking
</Button> </Button>
</NextLink> </NextLink>
<NextLink href="/account/settings/options" className="text-inherit"> <NextLink
href="/src/app/(main)/account/settings/options"
className="text-inherit"
>
<Button <Button
className="mb-[2px] w-[250px] " className="mb-[2px] w-[250px] "
variant={ variant={
@ -93,7 +99,10 @@ export function Sidebar({
<ResizableHandle className="max-md:hidden" /> <ResizableHandle className="max-md:hidden" />
<ResizablePanel> <ResizablePanel>
<div className="md:hidden ml-2"> <div className="md:hidden ml-2">
<NextLink href="/account/settings" className="text-inherit"> <NextLink
href="/src/app/(main)/account/settings"
className="text-inherit"
>
<Button <Button
className="mr-[2px]" className="mr-[2px]"
variant={curPage !== "/account/settings" ? "ghost" : "default"} variant={curPage !== "/account/settings" ? "ghost" : "default"}
@ -101,7 +110,10 @@ export function Sidebar({
<Link size={16} className="mr-2" /> Linking <Link size={16} className="mr-2" /> Linking
</Button> </Button>
</NextLink> </NextLink>
<NextLink href="/account/settings/options" className="text-inherit"> <NextLink
href="/src/app/(main)/account/settings/options"
className="text-inherit"
>
<Button <Button
className="mr-[2px]" className="mr-[2px]"
variant={ variant={

@ -28,32 +28,15 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { NewChart } from "@/components/NewChart";
import { MiniJoinsChart } from "@/components/misc/MiniJoinsChart";
import { import {
ContextMenu, ContextMenu,
ContextMenuTrigger,
ContextMenuItem,
ContextMenuContent, ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator, ContextMenuSeparator,
ContextMenuTrigger,
} from "@/components/ui/context-menu"; } from "@/components/ui/context-menu";
import toast, { LoaderIcon } from "react-hot-toast";
import {
CardHeader,
CardTitle,
CardDescription,
Card,
CardContent,
} from "./ui/card";
import IconDisplay from "./IconDisplay";
import { TagShower } from "./ServerList";
import {
ArrowRight,
ChartArea,
Copy,
EllipsisVertical,
Layers,
Star,
} from "lucide-react";
import { Button } from "./ui/button";
import { import {
Drawer, Drawer,
DrawerContent, DrawerContent,
@ -62,20 +45,39 @@ import {
DrawerTitle, DrawerTitle,
DrawerTrigger, DrawerTrigger,
} from "@/components/ui/drawer"; } from "@/components/ui/drawer";
import { Tooltip } from "@radix-ui/react-tooltip";
import { TooltipContent, TooltipTrigger } from "./ui/tooltip";
import { useRouter } from "@/lib/useRouter";
import Link from "next/link";
import { useState } from "react";
import { favoriteServer, isFavorited } from "@/lib/api";
import { useUser } from "@clerk/nextjs";
import { useTheme } from "next-themes";
import { import {
HoverCard, HoverCard,
HoverCardContent, HoverCardContent,
HoverCardTrigger, HoverCardTrigger,
} from "@/components/ui/hover-card"; } from "@/components/ui/hover-card";
import { favoriteServer, isFavorited } from "@/lib/api";
import useClipboard from "@/lib/useClipboard"; import useClipboard from "@/lib/useClipboard";
import { useRouter } from "@/lib/useRouter";
import { useUser } from "@clerk/nextjs";
import { Tooltip } from "@radix-ui/react-tooltip";
import {
ArrowRight,
ChartArea,
Copy,
EllipsisVertical,
Layers,
Star,
} from "lucide-react";
import { useTheme } from "next-themes";
import Link from "next/link";
import { useState } from "react";
import toast, { LoaderIcon } from "react-hot-toast";
import IconDisplay from "./IconDisplay";
import { TagShower } from "./ServerList";
import { Button } from "./ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "./ui/card";
import { TooltipContent, TooltipTrigger } from "./ui/tooltip";
export default function ServerCard({ b, motd, mini, favs }: any) { export default function ServerCard({ b, motd, mini, favs }: any) {
const router = useRouter(); const router = useRouter();
@ -130,6 +132,13 @@ export default function ServerCard({ b, motd, mini, favs }: any) {
/> />
)} )}
</p> </p>
<br />
<br />
<strong className="text-sm font-semibold text-center">
Joins Chart
</strong>
<MiniJoinsChart server={b.name} />
<div className="flex items-center pt-2"> <div className="flex items-center pt-2">
<span className="text-xs text-muted-foreground flex items-center"> <span className="text-xs text-muted-foreground flex items-center">
<ArrowRight size={16} className="mr-2" /> <ArrowRight size={16} className="mr-2" />
@ -272,7 +281,7 @@ export default function ServerCard({ b, motd, mini, favs }: any) {
</div> </div>
</ContextMenuItem> </ContextMenuItem>
<ContextMenuSeparator /> <ContextMenuSeparator />
<Link href={"/server/" + b.name}> <Link href={"/src/app/(main)/server/" + b.name}>
<ContextMenuItem>Open server page</ContextMenuItem> <ContextMenuItem>Open server page</ContextMenuItem>
</Link> </Link>
</ContextMenuContent> </ContextMenuContent>

@ -181,7 +181,7 @@ export default function ServerCustomize({
Is this server in violation of the ECA? Is this server in violation of the ECA?
</div> </div>
Is this server in violation of the{" "} Is this server in violation of the{" "}
<Link href="/docs/legal/external-content-agreement"> <Link href="/src/app/(main)/docs/legal/external-content-agreement">
External Content Agreement (aka ECA) External Content Agreement (aka ECA)
</Link> </Link>
? You can report the server to remove the customizations from the ? You can report the server to remove the customizations from the
@ -196,7 +196,7 @@ export default function ServerCustomize({
<DialogDescription> <DialogDescription>
This will send a notification to MHSF maintainers. This This will send a notification to MHSF maintainers. This
server must be in violation of the{" "} server must be in violation of the{" "}
<Link href="/docs/legal/external-content-agreement"> <Link href="/src/app/(main)/docs/legal/external-content-agreement">
ECA ECA
</Link>{" "} </Link>{" "}
to be a valid report. Typical response times include 1 hour to be a valid report. Typical response times include 1 hour
@ -329,7 +329,7 @@ export default function ServerCustomize({
Minehuts Terms of Service Minehuts Terms of Service
</Link>{" "} </Link>{" "}
& the{" "} & the{" "}
<Link href="/docs/legal/external-content-agreement"> <Link href="/src/app/(main)/docs/legal/external-content-agreement">
External Content Agreement External Content Agreement
</Link> </Link>
. .
@ -436,7 +436,7 @@ export default function ServerCustomize({
Imgurs Terms of Service Imgurs Terms of Service
</Link>{" "} </Link>{" "}
& the{" "} & the{" "}
<Link href="/docs/legal/external-content-agreement"> <Link href="/src/app/(main)/docs/legal/external-content-agreement">
External Content Agreement External Content Agreement
</Link> </Link>
. .
@ -468,7 +468,7 @@ export default function ServerCustomize({
Discords Terms of Service Discords Terms of Service
</Link>{" "} </Link>{" "}
& the{" "} & the{" "}
<Link href="/docs/legal/external-content-agreement"> <Link href="/src/app/(main)/docs/legal/external-content-agreement">
External Content Agreement External Content Agreement
</Link> </Link>
. .

@ -29,23 +29,8 @@
*/ */
"use client"; "use client";
import { useEffect, useRef, useState } from "react"; import { BorderBeam } from "@/components/effects/border-beam";
import { Separator } from "@/components/ui/separator";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Badge } from "./ui/badge";
import ServersList from "@/lib/list";
import {
CircleUser,
Network,
Sun,
Check,
XIcon,
Info,
ArrowDownZA,
LogIn,
ImageIcon,
} from "lucide-react";
import Stat from "./Stat";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@ -54,23 +39,6 @@ import {
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import toast from "react-hot-toast";
import { allTags, allCategories } from "@/config/tags";
import IconDisplay from "./IconDisplay";
import InfiniteScroll from "react-infinite-scroll-component";
import { Spinner } from "./ui/spinner";
import { CommandIcon } from "lucide-react";
import { OnlineServer } from "@/lib/types/mh-server";
import { useEffectOnce } from "@/lib/useEffectOnce";
import ServerCard from "./ServerCard";
import events from "@/lib/commandEvent";
import { BorderBeam } from "@/components/effects/border-beam";
import { useRouter } from "@/lib/useRouter";
import { import {
Menubar, Menubar,
MenubarCheckboxItem, MenubarCheckboxItem,
@ -86,21 +54,53 @@ import {
MenubarSubTrigger, MenubarSubTrigger,
MenubarTrigger, MenubarTrigger,
} from "@/components/ui/menubar"; } from "@/components/ui/menubar";
import ClientFadeIn from "./ClientFadeIn"; import { Separator } from "@/components/ui/separator";
import { Skeleton } from "./ui/skeleton"; import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { allCategories, allTags } from "@/config/tags";
import events from "@/lib/commandEvent";
import ServersList from "@/lib/list";
import { OnlineServer } from "@/lib/types/mh-server";
import useClipboard from "@/lib/useClipboard"; import useClipboard from "@/lib/useClipboard";
import { SignedIn, SignedOut, useUser } from "@clerk/nextjs"; import { useEffectOnce } from "@/lib/useEffectOnce";
import Link from "next/link"; import { useRouter } from "@/lib/useRouter";
import SparklesText from "./effects/sparkles-text";
import Particles from "./effects/particles";
import { useTheme } from "next-themes";
import { ChatBubbleIcon, InputIcon } from "@radix-ui/react-icons";
import Marquee from "./effects/marquee";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { SignedIn, SignedOut, useUser } from "@clerk/nextjs";
import { ChatBubbleIcon, InputIcon } from "@radix-ui/react-icons";
import {
ArrowDownZA,
Check,
CircleUser,
ImageIcon,
Info,
LogIn,
Network,
Sun,
XIcon,
} from "lucide-react";
import { CommandIcon } from "lucide-react";
import { useTheme } from "next-themes";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
import toast from "react-hot-toast";
import InfiniteScroll from "react-infinite-scroll-component";
import ClientFadeIn from "./ClientFadeIn";
import IconDisplay from "./IconDisplay";
import ServerCard from "./ServerCard";
import Stat from "./Stat";
import { SignInPopover } from "./clerk/SignInPopoverButton"; import { SignInPopover } from "./clerk/SignInPopoverButton";
import { BentoCard, BentoGrid } from "./effects/bento-grid"; import { BentoCard, BentoGrid } from "./effects/bento-grid";
import Marquee from "./effects/marquee";
import Particles from "./effects/particles";
import SparklesText from "./effects/sparkles-text";
import { pageFind } from "./misc/Link"; import { pageFind } from "./misc/Link";
import { Badge } from "./ui/badge";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Skeleton } from "./ui/skeleton";
import { Spinner } from "./ui/spinner";
const features = [ const features = [
{ {
@ -167,7 +167,7 @@ export default function ServerList() {
Array<(server: OnlineServer) => Promise<boolean>> Array<(server: OnlineServer) => Promise<boolean>>
>([]); >([]);
const [randomData, setRandomData] = useState<OnlineServer | undefined>( const [randomData, setRandomData] = useState<OnlineServer | undefined>(
undefined undefined,
); );
const { resolvedTheme } = useTheme(); const { resolvedTheme } = useTheme();
const [color, setColor] = useState("#ffffff"); const [color, setColor] = useState("#ffffff");
@ -258,7 +258,7 @@ export default function ServerList() {
<div className="p-0 branding-hero"> <div className="p-0 branding-hero">
<> <>
{(!isSignedIn || hero) && ( {(!isSignedIn || hero) && (
<div className=" pb-[300px] relative mx-auto mt-20 max-w-7xl px-6 text-center md:px-8 "> <div className="pb-[300px] max-lg:pb-[1000px] max-md:pb-[1000px] relative mx-auto mt-20 max-w-7xl text-center md:px-8 ">
<Particles <Particles
className="absolute inset-0 -z-10 block" className="absolute inset-0 -z-10 block"
quantity={100} quantity={100}
@ -349,7 +349,7 @@ export default function ServerList() {
className={cn( className={cn(
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " + "relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " +
"border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " + "border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " +
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]" "dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]",
)} )}
onClick={() => onClick={() =>
router.push(pageFind(`Server:${server.name}`)) router.push(pageFind(`Server:${server.name}`))
@ -383,7 +383,7 @@ export default function ServerList() {
className={cn( className={cn(
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " + "relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " +
"border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " + "border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " +
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]" "dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]",
)} )}
onClick={() => router.push(`/server/${server.name}`)} onClick={() => router.push(`/server/${server.name}`)}
> >
@ -566,7 +566,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
} },
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -582,7 +582,7 @@ export default function ServerList() {
success: "Succesfully refreshed servers", success: "Succesfully refreshed servers",
loading: "Refreshing...", loading: "Refreshing...",
error: "Error while refreshing", error: "Error while refreshing",
} },
); );
}} }}
> >
@ -629,7 +629,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
} },
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -668,7 +668,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
} },
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -707,7 +707,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
} },
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -720,7 +720,7 @@ export default function ServerList() {
error: "Error while changing filters", error: "Error while changing filters",
loading: "Changing filters...", loading: "Changing filters...",
success: "Changed filters!", success: "Changed filters!",
} },
); );
}} }}
value={(() => { value={(() => {
@ -804,7 +804,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
} },
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -901,7 +901,7 @@ export default function ServerList() {
Change your preferences Change your preferences
</Button> </Button>
</span>, </span>,
{ icon: "⚠️" } { icon: "!" },
); );
setIPR(v); setIPR(v);
}} }}
@ -939,7 +939,7 @@ export default function ServerList() {
Change your preferences Change your preferences
</Button> </Button>
</span>, </span>,
{ icon: "⚠️" } { icon: "!" },
); );
setPadding(v); setPadding(v);
}} }}
@ -1063,7 +1063,7 @@ export default function ServerList() {
onClick={() => { onClick={() => {
setTextCopied(true); setTextCopied(true);
clipboard.writeText( clipboard.writeText(
randomData.name + ".mshf.minehut.gg" randomData.name + ".mshf.minehut.gg",
); );
toast.success("Copied!"); toast.success("Copied!");
setTimeout(() => setTextCopied(false), 1000); setTimeout(() => setTextCopied(false), 1000);

@ -30,17 +30,17 @@
"use client"; "use client";
import { ClerkProvider } from "@clerk/nextjs"; import { ClerkProvider } from "@clerk/nextjs";
import { useTheme } from "next-themes";
import { dark } from "@clerk/themes"; import { dark } from "@clerk/themes";
import { useTheme } from "next-themes";
import { type ReactNode, useEffect, useState } from "react";
import { ThemeProvider } from "../ThemeProvider"; import { ThemeProvider } from "../ThemeProvider";
import { useEffect, useState } from "react";
export function ClerkThemeProvider({ export function ClerkThemeProvider({
children, children,
className, className,
}: { }: {
children: JSX.Element; children: ReactNode | ReactNode[];
className: string; className: string | undefined;
}) { }) {
const [theme, setTheme] = useState<string | undefined>(""); const [theme, setTheme] = useState<string | undefined>("");

@ -0,0 +1,199 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
"use client";
import {
BadgeCheck,
Bell,
ChevronsUpDown,
Computer,
CreditCard,
LogIn,
LogOut,
Moon,
SettingsIcon,
Sparkles,
Sun,
} from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from "@/components/ui/sidebar";
import { useClerk, useUser } from "@clerk/nextjs";
import { useTheme } from "next-themes";
export function NavUser() {
const { isMobile } = useSidebar();
const { user, isSignedIn } = useUser();
const clerk = useClerk();
const { setTheme, theme } = useTheme();
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
{isSignedIn && (
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage
src={
user?.imageUrl == undefined
? "https://img.clerk.com/preview.png?size=144&seed=seed&initials=AD&isSquare=true&bgType=marble&bgColor=6c47ff&fgType=silhouette&fgColor=FFFFFF&type=user&w=48&q=75"
: user?.imageUrl
}
alt={user?.username || "?"}
/>
<AvatarFallback className="rounded-lg">?</AvatarFallback>
</Avatar>
)}
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{isSignedIn ? user?.username : "Not logged in"}
</span>
<span className="truncate text-xs">
{user?.primaryEmailAddress?.emailAddress}
</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side={isMobile ? "bottom" : "right"}
align="end"
sideOffset={4}
>
{isSignedIn && (
<>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage
src={
user?.imageUrl == undefined
? "https://img.clerk.com/preview.png?size=144&seed=seed&initials=AD&isSquare=true&bgType=marble&bgColor=6c47ff&fgType=silhouette&fgColor=FFFFFF&type=user&w=48&q=75"
: user?.imageUrl
}
alt={user?.username || "?"}
/>
<AvatarFallback className="rounded-lg">?</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{user?.username}
</span>
<span className="truncate text-xs">
{user?.primaryEmailAddress?.emailAddress}
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
</>
)}
<DropdownMenuGroup>
<DropdownMenuRadioGroup
onValueChange={(c) => setTheme(c)}
value={theme}
>
<DropdownMenuRadioItem value="dark">
<Moon /> Dark
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="light">
<Sun /> Light
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="system">
<Computer /> System
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuGroup>
{isSignedIn && (
<>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem onSelect={() => clerk.openUserProfile()}>
<SettingsIcon />
Account Settings
</DropdownMenuItem>
<DropdownMenuItem
className="text-red-500"
onSelect={() => clerk.signOut()}
>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuGroup>
</>
)}
{!isSignedIn && (
<>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem onSelect={() => clerk.openSignIn()}>
<LogIn />
Sign in
</DropdownMenuItem>
</DropdownMenuGroup>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
}

@ -1,134 +1,51 @@
/* import * as React from "react";
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
"use client"; import { NavUser } from "@/components/docs/NavUser";
import { allFolders, Docs, DocsFolder } from "@/config/docs"; import { VersionSwitcher } from "@/components/docs/VersionSwitcher";
import { usePathname } from "next/navigation"; import {
import { Button } from "../ui/button"; Sidebar as ShadSidebar,
import { useState } from "react"; SidebarContent,
import { ChevronRight } from "lucide-react"; SidebarFooter,
import { useRouter } from "@/lib/useRouter"; SidebarGroup,
import { AnimatePresence, motion } from "framer-motion"; SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
} from "@/components/ui/sidebar";
import { allFolders } from "@/config/docs";
export function Sidebar() { export function Sidebar() {
return ( return (
<> <ShadSidebar>
{allFolders.map((docs) => ( <SidebarHeader>
<Folder docs={docs} key={"url" in docs ? docs.title : docs.name} /> <VersionSwitcher />
</SidebarHeader>
<SidebarContent>
{/* We create a SidebarGroup for each parent. */}
{allFolders.map((item) => (
<SidebarGroup key={item.name}>
<SidebarGroupLabel>{item.name}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{item.docs.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>{item.title}</a>
</SidebarMenuButton>
</SidebarMenuItem>
))} ))}
</> </SidebarMenu>
); </SidebarGroupContent>
} </SidebarGroup>
))}
function Folder({ docs }: { docs: any }) { </SidebarContent>
const [folderOpen, setOpen] = useState(false); <SidebarFooter>
const router = useRouter(); <NavUser />
const pathname = usePathname(); </SidebarFooter>
<SidebarRail />
return ( </ShadSidebar>
<div key={"url" in docs ? docs.title : docs.name}>
<Button
size="sm"
className="w-full font-normal tracking-normal mt-1"
noJustify
variant={
"url" in docs
? pathname === docs.url
? "default"
: "ghost"
: "ghost"
}
onClick={() => {
if ("docs" in docs) {
setOpen(!folderOpen);
} else {
router.push(docs.url);
}
}}
>
{"url" in docs ? docs.title : docs.name}
<div className="flex items-center ml-auto text-muted-foreground">
<AnimatePresence>
{"docs" in docs && folderOpen && (
<motion.div initial={{ rotate: 90 }} animate={{ rotate: 0 }}>
<ChevronRight size={18} />
</motion.div>
)}
{"docs" in docs && !folderOpen && (
<motion.div initial={{ rotate: 0 }} animate={{ rotate: 90 }}>
<ChevronRight size={18} />
</motion.div>
)}
</AnimatePresence>
</div>
</Button>
<div className="ml-2">
{folderOpen && <Subdocs docs={"docs" in docs ? docs.docs : []} />}
</div>
</div>
);
}
function Subdocs({ docs }: { docs: (Docs | DocsFolder)[] }) {
const pathname = usePathname();
const router = useRouter();
return (
<>
{docs.map((doc) => {
if ("docs" in doc) {
return <Subdocs docs={doc.docs} key={doc.name} />;
}
return (
<>
<Button
size="sm"
className="w-full font-normal tracking-normal mt-1"
noJustify
onClick={() => {
router.push(doc.url);
}}
key={doc.title}
variant={
"url" in doc
? pathname == doc.url
? "default"
: "ghost"
: "ghost"
}
>
{doc.title}
</Button>
<br key={doc.url} />
</>
);
})}
</>
); );
} }

@ -28,17 +28,17 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { Button } from "../ui/button"; import { Changelog } from "@/components/changelog";
import { Book, Calendar, Star, TerminalIcon } from "lucide-react"; import { changelog, version } from "@/config/version";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog";
import { useState } from "react";
import { Changelog, version } from "@/config/version";
import events from "@/lib/commandEvent"; import events from "@/lib/commandEvent";
import { ScrollArea } from "../ui/scroll-area";
import { useRouter } from "@/lib/useRouter"; import { useRouter } from "@/lib/useRouter";
import { Book, Calendar, Star, TerminalIcon } from "lucide-react";
import { useState } from "react";
import { Button } from "../ui/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog";
import { ScrollArea } from "../ui/scroll-area";
export default function InfoPopover() { export default function InfoPopover() {
const [changeLog, setChangelog] = useState(false);
const router = useRouter(); const router = useRouter();
return ( return (
@ -51,19 +51,11 @@ export default function InfoPopover() {
<small>* Licensed under the MIT License</small> <small>* Licensed under the MIT License</small>
</small> </small>
<Button variant={"ghost"} onClick={() => setChangelog(true)}> <Changelog items={changelog}>
<Button variant={"ghost"}>
<Calendar size={18} className="mr-2" /> Changelog <Calendar size={18} className="mr-2" /> Changelog
</Button> </Button>
<Dialog open={changeLog} onOpenChange={setChangelog}> </Changelog>
<DialogContent>
<ScrollArea className="max-h-[500px]">
<DialogHeader>
<DialogTitle>Changelog</DialogTitle>
<Changelog />
</DialogHeader>
</ScrollArea>
</DialogContent>
</Dialog>
<Button <Button
variant={"ghost"} variant={"ghost"}

@ -0,0 +1,111 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { getShortTermData } from "@/lib/api";
import { useEffectOnce } from "@/lib/useEffectOnce";
import * as React from "react";
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
const chartConfig = {
player_count: {
label: "Joins",
color: "hsl(var(--chart-1))",
},
} satisfies ChartConfig;
export function MiniJoinsChart({ server }: { server: string }) {
const [chartData, setChartData] = React.useState<any>([]);
const [loading, setLoading] = React.useState(true);
useEffectOnce(() => {
getShortTermData(server, ["player_count", "date"]).then((result) => {
setChartData(result.slice(-20));
setLoading(false);
});
});
return (
<div className="max-h-[160px] w-full">
<ChartContainer config={chartConfig}>
<LineChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<YAxis
dataKey={"player_count"}
tickLine={false}
axisLine={false}
tickFormatter={(value) => {
return value;
}}
/>
<XAxis
dataKey="date"
className="hidden"
tickLine={false}
axisLine={false}
tickMargin={8}
minTickGap={32}
tickFormatter={(value) => {
return new Date(value).toLocaleTimeString("en-US", {
timeStyle: "short",
});
}}
/>
<ChartTooltip
content={
<ChartTooltipContent
className="w-[150px]"
nameKey={"player_count"}
indicator="line"
labelFormatter={(value) => {
return new Date(value).toLocaleTimeString("en-US", {
timeStyle: "short",
});
}}
/>
}
/>
<Line
dataKey={"player_count"}
type="monotone"
stroke={"var(--color-player_count)"}
strokeWidth={2}
dot={false}
/>
</LineChart>
</ChartContainer>
</div>
);
}

@ -28,30 +28,30 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
"use client" "use client";
import * as React from "react" import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" import { Check, ChevronRight, Circle } from "lucide-react";
import { Check, ChevronRight, Circle } from "lucide-react" import * as React from "react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const DropdownMenu = DropdownMenuPrimitive.Root const DropdownMenu = DropdownMenuPrimitive.Root;
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
const DropdownMenuGroup = DropdownMenuPrimitive.Group const DropdownMenuGroup = DropdownMenuPrimitive.Group;
const DropdownMenuPortal = DropdownMenuPrimitive.Portal const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
const DropdownMenuSub = DropdownMenuPrimitive.Sub const DropdownMenuSub = DropdownMenuPrimitive.Sub;
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
const DropdownMenuSubTrigger = React.forwardRef< const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>, React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, children, ...props }, ref) => ( >(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger <DropdownMenuPrimitive.SubTrigger
@ -59,16 +59,16 @@ const DropdownMenuSubTrigger = React.forwardRef<
className={cn( className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent", "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
<ChevronRight className="ml-auto h-4 w-4" /> <ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger> </DropdownMenuPrimitive.SubTrigger>
)) ));
DropdownMenuSubTrigger.displayName = DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName DropdownMenuPrimitive.SubTrigger.displayName;
const DropdownMenuSubContent = React.forwardRef< const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>, React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
@ -78,13 +78,13 @@ const DropdownMenuSubContent = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
)) ));
DropdownMenuSubContent.displayName = DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName DropdownMenuPrimitive.SubContent.displayName;
const DropdownMenuContent = React.forwardRef< const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>, React.ElementRef<typeof DropdownMenuPrimitive.Content>,
@ -96,31 +96,31 @@ const DropdownMenuContent = React.forwardRef<
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
</DropdownMenuPrimitive.Portal> </DropdownMenuPrimitive.Portal>
)) ));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
const DropdownMenuItem = React.forwardRef< const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>, React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, ...props }, ref) => ( >(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
)) ));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
const DropdownMenuCheckboxItem = React.forwardRef< const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>, React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
@ -130,7 +130,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className className,
)} )}
checked={checked} checked={checked}
{...props} {...props}
@ -142,9 +142,9 @@ const DropdownMenuCheckboxItem = React.forwardRef<
</span> </span>
{children} {children}
</DropdownMenuPrimitive.CheckboxItem> </DropdownMenuPrimitive.CheckboxItem>
)) ));
DropdownMenuCheckboxItem.displayName = DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName DropdownMenuPrimitive.CheckboxItem.displayName;
const DropdownMenuRadioItem = React.forwardRef< const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>, React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
@ -153,8 +153,8 @@ const DropdownMenuRadioItem = React.forwardRef<
<DropdownMenuPrimitive.RadioItem <DropdownMenuPrimitive.RadioItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0 gap-3",
className className,
)} )}
{...props} {...props}
> >
@ -165,13 +165,13 @@ const DropdownMenuRadioItem = React.forwardRef<
</span> </span>
{children} {children}
</DropdownMenuPrimitive.RadioItem> </DropdownMenuPrimitive.RadioItem>
)) ));
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
const DropdownMenuLabel = React.forwardRef< const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>, React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, ...props }, ref) => ( >(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label <DropdownMenuPrimitive.Label
@ -179,12 +179,12 @@ const DropdownMenuLabel = React.forwardRef<
className={cn( className={cn(
"px-2 py-1.5 text-sm font-semibold", "px-2 py-1.5 text-sm font-semibold",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
)) ));
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
const DropdownMenuSeparator = React.forwardRef< const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>, React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
@ -195,8 +195,8 @@ const DropdownMenuSeparator = React.forwardRef<
className={cn("-mx-1 my-1 h-px bg-muted", className)} className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props} {...props}
/> />
)) ));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
const DropdownMenuShortcut = ({ const DropdownMenuShortcut = ({
className, className,
@ -207,9 +207,9 @@ const DropdownMenuShortcut = ({
className={cn("ml-auto text-xs tracking-widest opacity-60", className)} className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props} {...props}
/> />
) );
} };
DropdownMenuShortcut.displayName = "DropdownMenuShortcut" DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
export { export {
DropdownMenu, DropdownMenu,
@ -227,4 +227,4 @@ export {
DropdownMenuSubContent, DropdownMenuSubContent,
DropdownMenuSubTrigger, DropdownMenuSubTrigger,
DropdownMenuRadioGroup, DropdownMenuRadioGroup,
} };

@ -28,19 +28,20 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
export const allFolders: (DocsFolder | Docs)[] = [ export const allFolders: DocsFolder[] = [
{
name: "General",
docs: [
{ {
title: "Getting Started", title: "Getting Started",
url: "/docs/getting-started", url: "/docs/getting-started",
}, },
{
title: "Server List",
url: "/",
},
{ {
title: "Reading", title: "Reading",
url: "/docs/reading", url: "/docs/reading",
}, },
],
},
{ {
name: "Guides", name: "Guides",
docs: [ docs: [

@ -278,7 +278,7 @@ export const allCategories: Array<{
]; ];
async function requestServer(s: OnlineServer): Promise<ServerResponse> { async function requestServer(s: OnlineServer): Promise<ServerResponse> {
if (serverCache[s.name] == undefined) { if (serverCache[s.name] === undefined) {
const re = await fetch( const re = await fetch(
"https://api.minehut.com/server/" + s.name + "?byName=true", "https://api.minehut.com/server/" + s.name + "?byName=true",
); );

@ -29,149 +29,73 @@
*/ */
"use client"; "use client";
import Image from "next/image"; import A from "@/components/misc/Link";
import Link from "next/link"; import type { ReactNode } from "react";
import { Separator } from "../components/ui/separator";
import { Button } from "../components/ui/button";
import confetti from "canvas-confetti";
export const version = "1.3.2";
const User = ({ user }: { user: string }) => ( const User = ({ user }: { user: string }) => (
<span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]"> <span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]">
{user} {user}
</span> </span>
); );
const handleClick = () => {
const duration = 5 * 1000;
const animationEnd = Date.now() + duration;
const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 0 };
const randomInRange = (min: number, max: number) => const FeatureList = ({
Math.random() * (max - min) + min; features,
title,
const interval = window.setInterval(() => { }: { features: (string | ReactNode)[]; title: ReactNode }) => {
const timeLeft = animationEnd - Date.now();
if (timeLeft <= 0) {
return clearInterval(interval);
}
const particleCount = 50 * (timeLeft / duration);
confetti({
...defaults,
particleCount,
zIndex: 60,
origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 },
});
confetti({
...defaults,
particleCount,
zIndex: 60,
origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 },
});
}, 250);
};
export const Changelog = () => {
const router = useRouter();
return ( return (
<> <ul>
<div> {title}
Running on commit{" "} {features.map((feature, i) => (
<code> <li key={i}> {feature}</li>
<a ))}
href={`https://github.com/DeveloLongScript/mhsf/commit/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA}`} </ul>
> );
{( };
process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA || "unknown"
).substring(0, 7)} export const version = "1.4.0";
</a>{" "} export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
{process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID != undefined && {
process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID != "" && ( id: "amq4suhgcfwrb7y5j6",
<> name: "v1.4.0",
{" "} changelog: (
| on PR{" "} <FeatureList
<a features={[
href={`https://github.com/DeveloLongScript/MHSF/pull/${process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID}`} "Revamped documentation",
> "Revamped changelog UI",
{process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID} "New hover joins chart",
</a>{" "} ]}
by{" "} title={
<a <strong className="flex items-center">
href={`https://github.com/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME}`} Version 1.4.0 (November 3rd 2024)
> </strong>
{process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME}
</a>
</>
)}{" "}
{process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE != undefined &&
`| ${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE.substring(0, 24)}`}
</code>
</div>
<div className="md:grid md:grid-cols-3 gap-1.5">
<Button
className="text-sm hover:h-[60px] animate-all group block max-md:w-full max-md:mt-2"
onClick={() =>
window.open("https://discord.com/invite/cCyEeUs", "_blank")
} }
> />
<span className="group-hover:underline flex items-center"> ),
<Discord className="mr-2"/> Discord },
</span> {
<Marquee id: "jeh48p7w9bx2k3ad6f",
className="hidden group-hover:flex font-normal" name: "v1.3.2",
style={{"--duration": "15s"}} changelog: (
>
Join the offical Minehut Discord server! Talk to people that like
MHSF too!
</Marquee>
</Button>
<Button
className="text-sm max-md:w-full max-md:mt-2 hover:h-[60px] animate-all group block "
onClick={() =>
window.open("https://github.com/DeveloLongScript/MHSF", "_blank")
}
>
<span className="group-hover:underline flex items-center">
<Github className="mr-2" fill={useDepTheme()}/> Star on GitHub
</span>
<Marquee
className="hidden group-hover:flex font-normal"
style={{"--duration": "10s"}}
>
Support the development of MHSF by starring it on GitHub!
</Marquee>
</Button>
<Button
className="text-sm max-md:w-full max-md:mt-2 hover:h-[60px] animate-all group block "
onClick={() => window.open("/docs", "_blank")}
>
<span className="group-hover:underline flex items-center">
<BookIcon className="mr-2" size={16}/> See the docs
</span>
<Marquee
className="hidden group-hover:flex font-normal"
style={{"--duration": "10s"}}
>
See more information about MHSF and how to use it
</Marquee>
</Button>
</div>
<br/>
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version 1.3.2 (October 4th 2024) Version 1.3.2 (October 4th 2024)
</strong> </strong>
<ul> <ul>
<li> Minor backend changes</li>
<li> <li>
Minor backend changes {" "}
</li> <A alt="Please check on GitHub for statuses about this project.">
<li> Special:GitHub/releases/tag/1.3.2
<A alt="Please check on GitHub for statuses about this project.">Special:GitHub/releases/tag/1.3.2</A> </A>
</li> </li>
</ul> </ul>
</div> </div>
<br/> ),
},
{
id: "wvg9x5dbpj76sn4yrz",
name: "v1.3.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version 1.3.0 (September 9th 2024) Version 1.3.0 (September 9th 2024)
@ -188,7 +112,11 @@ export const Changelog = () => {
<li> Overhauled account preferences</li> <li> Overhauled account preferences</li>
</ul> </ul>
</div> </div>
<br/> ),
},
{
name: "v1.2.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version 1.2.0 (September 3rd 2024) Version 1.2.0 (September 3rd 2024)
@ -200,7 +128,12 @@ export const Changelog = () => {
<li> and alot more!</li> <li> and alot more!</li>
</ul> </ul>
</div> </div>
<br/> ),
id: "e482y9k5hvjt73urfx",
},
{
name: "v1.1.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version 1.1.0 (August 24rd 2024) Version 1.1.0 (August 24rd 2024)
@ -211,27 +144,29 @@ export const Changelog = () => {
<li> New help guide</li> <li> New help guide</li>
</ul> </ul>
</div> </div>
<br/> ),
id: "hfn9p243765x8bwurj",
},
{
name: "v1.0.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version 1.0.0 (August 22nd 2024) Version 1.0.0 (August 22nd 2024)
</strong> </strong>
<ul> <ul>
<li> <li> 1.0!</li>
1.0!{" "}
<Button className="h-[25px] w-[50px] ml-2" onClick={handleClick}>
woah!
</Button>
</li>
<li> New hover card on server title hover</li> <li> New hover card on server title hover</li>
<li> Moving to self-hosted cron jobs</li> <li> Moving to self-hosted cron jobs</li>
<li> Fixing some mobile issues</li> <li> Fixing some mobile issues</li>
</ul> </ul>
</div> </div>
<br/> ),
<Separator/> id: "a8w4xvjbg3s7ynehu6",
},
<br/> {
name: "v0.10.7",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.10.7 (August 18th 2024) Version b-0.10.7 (August 18th 2024)
@ -240,7 +175,12 @@ export const Changelog = () => {
<li> New server information tab on server pages</li> <li> New server information tab on server pages</li>
</ul> </ul>
</div> </div>
<br/> ),
id: "asbt64h9fdyu8neqmp",
},
{
name: "v0.10.2",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.10.2 (August 18th 2024) Version b-0.10.2 (August 18th 2024)
@ -250,7 +190,12 @@ export const Changelog = () => {
<li> Instead of using spinners, now we are using Skeletons</li> <li> Instead of using spinners, now we are using Skeletons</li>
</ul> </ul>
</div> </div>
<br/> ),
id: "kct29adbp6zug5r3q8",
},
{
name: "v0.10.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.10.0 (August 17th 2024) Version b-0.10.0 (August 17th 2024)
@ -271,8 +216,12 @@ export const Changelog = () => {
<span className="grid-cols-5" /> <span className="grid-cols-5" />
<span className="grid-cols-4" /> <span className="grid-cols-4" />
</div> </div>
<br/> ),
<br/> id: "ah6t7c8sfzyrkp3u52",
},
{
name: "v0.9.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.9.0 (August 15th 2024) Version b-0.9.0 (August 15th 2024)
@ -291,7 +240,12 @@ export const Changelog = () => {
</i> </i>
<br /> <br />
</div> </div>
<br/> ),
id: "kjxnrfazc7hp9q4e82",
},
{
name: "v0.8.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.8.0 (August 11th 2024) Version b-0.8.0 (August 11th 2024)
@ -301,7 +255,12 @@ export const Changelog = () => {
<li> Renaming "Short Term" to "Statistics"</li> <li> Renaming "Short Term" to "Statistics"</li>
</ul> </ul>
</div> </div>
<br/> ),
id: "f8rmhwzuxk3qyds542",
},
{
name: "v0.7.2",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.7.2 (August 7th 2024) Version b-0.7.2 (August 7th 2024)
@ -312,7 +271,12 @@ export const Changelog = () => {
<li> Moved from Inngest to Vercel Cron</li> <li> Moved from Inngest to Vercel Cron</li>
</ul> </ul>
</div> </div>
<br/> ),
id: "g2rhxfj6bu8wqk43n7",
},
{
name: "v0.7.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.7.0 (August 7th 2024) Version b-0.7.0 (August 7th 2024)
@ -323,7 +287,12 @@ export const Changelog = () => {
<li> Lots of bugfixes</li> <li> Lots of bugfixes</li>
</ul> </ul>
</div> </div>
<br/> ),
id: "a5xb97jv3surwmqn62",
},
{
name: "v0.6.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.6.0 (August 3rd 2024) Version b-0.6.0 (August 3rd 2024)
@ -335,7 +304,12 @@ export const Changelog = () => {
<li> Lots of bugfixes</li> <li> Lots of bugfixes</li>
</ul> </ul>
</div> </div>
<br/> ),
id: "u83r5mkea9x4p2fjnb",
},
{
name: "v0.4.5",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.4.5 (July 26th 2024): Version b-0.4.5 (July 26th 2024):
@ -345,7 +319,12 @@ export const Changelog = () => {
<li> Sorted API endpoints</li> <li> Sorted API endpoints</li>
</ul> </ul>
</div> </div>
<br/> ),
id: "vu3k2daqj4y68bnwsp",
},
{
name: "v0.4.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.4 (July 25th 2024): Version b-0.4 (July 25th 2024):
@ -359,7 +338,12 @@ export const Changelog = () => {
</li> </li>
</ul> </ul>
</div> </div>
<br/> ),
id: "psr9tx5jah74d32vq6",
},
{
name: "v0.3.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.3 (July 23th 2024): Version b-0.3 (July 23th 2024):
@ -370,7 +354,12 @@ export const Changelog = () => {
</li> </li>
</ul> </ul>
</div> </div>
<br/> ),
id: "m2ngpd6fwtv7xh5zrk",
},
{
name: "v0.2.0",
changelog: (
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.2 (July 23th 2024): Version b-0.2 (July 23th 2024):
@ -379,57 +368,7 @@ export const Changelog = () => {
<li> Inital release!</li> <li> Inital release!</li>
</ul> </ul>
</div> </div>
<br/> ),
<div> id: "xsfw2rcnv7m3kuhpbq",
<strong>All developers that helped out:</strong> },
<Link href="https://dvelo.vercel.app"> ];
<Image
src="/imgs/badge1.gif"
alt="cool badge"
width={88}
height={31}
className="w-[88px] h-[31px]"
/>
</Link>
</div>
</>
);
};
const Github = (props: SVGProps<SVGSVGElement>) => (
<svg
viewBox="0 0 256 250"
width="1em"
height="1em"
fill="#24292f"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
{...props}
>
<path
d="M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z" />
</svg>
);
import type { SVGProps } from "react";
import Marquee from "@/components/effects/marquee";
import { useRouter } from "@/lib/useRouter";
import { BookIcon } from "lucide-react";
import A from "@/components/misc/Link";
import { useDepTheme } from "@/lib/getDependentTheming";
const Discord = (props: SVGProps<SVGSVGElement>) => (
<svg
viewBox="0 0 256 199"
width="1em"
height="1em"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
{...props}
>
<path
d="M216.856 16.597A208.502 208.502 0 0 0 164.042 0c-2.275 4.113-4.933 9.645-6.766 14.046-19.692-2.961-39.203-2.961-58.533 0-1.832-4.4-4.55-9.933-6.846-14.046a207.809 207.809 0 0 0-52.855 16.638C5.618 67.147-3.443 116.4 1.087 164.956c22.169 16.555 43.653 26.612 64.775 33.193A161.094 161.094 0 0 0 79.735 175.3a136.413 136.413 0 0 1-21.846-10.632 108.636 108.636 0 0 0 5.356-4.237c42.122 19.702 87.89 19.702 129.51 0a131.66 131.66 0 0 0 5.355 4.237 136.07 136.07 0 0 1-21.886 10.653c4.006 8.02 8.638 15.67 13.873 22.848 21.142-6.58 42.646-16.637 64.815-33.213 5.316-56.288-9.08-105.09-38.056-148.36ZM85.474 135.095c-12.645 0-23.015-11.805-23.015-26.18s10.149-26.2 23.015-26.2c12.867 0 23.236 11.804 23.015 26.2.02 14.375-10.148 26.18-23.015 26.18Zm85.051 0c-12.645 0-23.014-11.805-23.014-26.18s10.148-26.2 23.014-26.2c12.867 0 23.236 11.804 23.015 26.2 0 14.375-10.148 26.18-23.015 26.18Z"
fill="#5865F2"
/>
</svg>
);

@ -30,10 +30,14 @@
import { LinearClient, LinearFetch, User } from "@linear/sdk"; import { LinearClient, LinearFetch, User } from "@linear/sdk";
export async function createReportIssue(server: string, reportDescription: string, userId: string) { export async function createReportIssue(
server: string,
reportDescription: string,
userId: string,
) {
const linearClient = new LinearClient({ const linearClient = new LinearClient({
apiKey: process.env.LINEAR apiKey: process.env.LINEAR,
}) });
const allTeams = await linearClient.teams(); const allTeams = await linearClient.teams();
// Always grabs the first issue category. // Always grabs the first issue category.
@ -41,7 +45,12 @@ export async function createReportIssue(server: string, reportDescription: strin
// Ensure there *actually* is a team there // Ensure there *actually* is a team there
if (team.id) { if (team.id) {
await linearClient.createIssue({teamId: team.id, title: `Issue against server \`${server}\``, description: desc(userId, server, reportDescription), assigneeId: (await team.members()).nodes[0].id }) await linearClient.createIssue({
teamId: team.id,
title: `Issue against server \`${server}\``,
description: desc(userId, server, reportDescription),
assigneeId: (await team.members()).nodes[0].id,
});
} }
} }
@ -56,4 +65,4 @@ ${reason}
*This was an automatically added issue by the report bot. Add the canceled status to remove the issue from the active issues, along with the labels Not Controllable & Spam for their respective values.* *This was an automatically added issue by the report bot. Add the canceled status to remove the issue from the active issues, along with the labels Not Controllable & Spam for their respective values.*
` `;

@ -0,0 +1,58 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 2024 dvelo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
export async function getMinehutIcons(): Promise<MinehutIcon[] | undefined> {
const icons = await fetch("https://api.minehut.com/servers/icons");
console.log(icons);
if (!icons.ok) return undefined;
return await icons.json();
}
export type MinehutIcon = {
_id: string;
display_name: string;
icon_name: string;
price: number;
rank: string;
available: boolean;
disabled: boolean;
created: number;
last_updated: number;
__v: number;
salePrice: any;
};
export const rarityIndex = {
common: { bg: "#40464d", text: "#b7bfc5" },
uncommon: { bg: "#184f02", text: "#61bf01" },
rare: { bg: "#15448a", text: "#41afff" },
epic: { bg: "#4c1a7b", text: "#ce59ff" },
legendary: { bg: "#de6e0d", text: "#fce8cf" },
};

@ -41,126 +41,165 @@ const config = {
prefix: "", prefix: "",
theme: { theme: {
container: { container: {
center: true, center: 'true',
padding: "2rem", padding: '2rem',
screens: { screens: {
"2xl": "1400px", '2xl': '1400px'
}, }
}, },
extend: { extend: {
colors: { colors: {
border: "hsl(var(--border))", border: 'hsl(var(--border))',
input: "hsl(var(--input))", input: 'hsl(var(--input))',
ring: "hsl(var(--ring))", ring: 'hsl(var(--ring))',
background: "hsl(var(--background))", background: 'hsl(var(--background))',
foreground: "hsl(var(--foreground))", foreground: 'hsl(var(--foreground))',
primary: { primary: {
DEFAULT: "hsl(var(--primary))", DEFAULT: 'hsl(var(--primary))',
foreground: "hsl(var(--primary-foreground))", foreground: 'hsl(var(--primary-foreground))'
}, },
secondary: { secondary: {
DEFAULT: "hsl(var(--secondary))", DEFAULT: 'hsl(var(--secondary))',
foreground: "hsl(var(--secondary-foreground))", foreground: 'hsl(var(--secondary-foreground))'
}, },
destructive: { destructive: {
DEFAULT: "hsl(var(--destructive))", DEFAULT: 'hsl(var(--destructive))',
foreground: "hsl(var(--destructive-foreground))", foreground: 'hsl(var(--destructive-foreground))'
}, },
muted: { muted: {
DEFAULT: "hsl(var(--muted))", DEFAULT: 'hsl(var(--muted))',
foreground: "hsl(var(--muted-foreground))", foreground: 'hsl(var(--muted-foreground))'
}, },
accent: { accent: {
DEFAULT: "hsl(var(--accent))", DEFAULT: 'hsl(var(--accent))',
foreground: "hsl(var(--accent-foreground))", foreground: 'hsl(var(--accent-foreground))'
}, },
popover: { popover: {
DEFAULT: "hsl(var(--popover))", DEFAULT: 'hsl(var(--popover))',
foreground: "hsl(var(--popover-foreground))", foreground: 'hsl(var(--popover-foreground))'
}, },
card: { card: {
DEFAULT: "hsl(var(--card))", DEFAULT: 'hsl(var(--card))',
foreground: "hsl(var(--card-foreground))", foreground: 'hsl(var(--card-foreground))'
}, },
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))',
mhsf: 'hsl(var(--sidebar-mhsf))'
}
}, },
borderRadius: { borderRadius: {
lg: "var(--radius)", lg: 'var(--radius)',
md: "calc(var(--radius) - 2px)", md: 'calc(var(--radius) - 2px)',
sm: "calc(var(--radius) - 4px)", sm: 'calc(var(--radius) - 4px)'
}, },
keyframes: { keyframes: {
'image-glow': { 'image-glow': {
'0%': { '0%': {
'opacity': '0', opacity: '0',
'animation-timing-function': 'cubic-bezier(0.74, 0.25, 0.76, 1)', 'animation-timing-function': 'cubic-bezier(0.74, 0.25, 0.76, 1)'
}, },
'10%': { '10%': {
'opacity': '0.7', opacity: '0.7',
'animation-timing-function': 'cubic-bezier(0.12, 0.01, 0.08, 0.99)', 'animation-timing-function': 'cubic-bezier(0.12, 0.01, 0.08, 0.99)'
}, },
'100%': { '100%': {
opacity: '0.4', opacity: '0.4'
}
}, },
'border-beam': {
'100%': {
'offset-distance': '100%'
}
}, },
"border-beam": { 'caret-blink': {
"100%": { '0%,70%,100%': {
"offset-distance": "100%", opacity: '1'
}, },
'20%,50%': {
opacity: '0'
}
}, },
"caret-blink": { 'accordion-down': {
"0%,70%,100%": { opacity: "1" }, from: {
"20%,50%": { opacity: "0" }, height: '0'
}, },
to: {
"accordion-down": { height: 'var(--radix-accordion-content-height)'
from: { height: "0" }, }
to: { height: "var(--radix-accordion-content-height)" },
}, },
"accordion-up": { 'accordion-up': {
from: { height: "var(--radix-accordion-content-height)" }, from: {
to: { height: "0" }, height: 'var(--radix-accordion-content-height)'
}, },
to: {
"fade-in": { height: '0'
from: { opacity: "0", transform: "translateY(-10px)" }, }
to: { opacity: "1", transform: "none" }, },
'fade-in': {
from: {
opacity: '0',
transform: 'translateY(-10px)'
},
to: {
opacity: '1',
transform: 'none'
}
}, },
marquee: { marquee: {
from: { transform: "translateX(0)" }, from: {
to: { transform: "translateX(calc(-100% - var(--gap)))" }, transform: 'translateX(0)'
}, },
"marquee-vertical": { to: {
from: { transform: "translateY(0)" }, transform: 'translateX(calc(-100% - var(--gap)))'
to: { transform: "translateY(calc(-100% - var(--gap)))" }, }
}, },
"fade-up": { 'marquee-vertical': {
from: { opacity: "0", transform: "translateY(20px)" }, from: {
to: { opacity: "1", transform: "none" }, transform: 'translateY(0)'
}, },
to: {
'shimmer': { transform: 'translateY(calc(-100% - var(--gap)))'
}
},
'fade-up': {
from: {
opacity: '0',
transform: 'translateY(20px)'
},
to: {
opacity: '1',
transform: 'none'
}
},
shimmer: {
'0%, 90%, 100%': { '0%, 90%, 100%': {
'background-position': 'calc(-100% - var(--shimmer-width)) 0', 'background-position': 'calc(-100% - var(--shimmer-width)) 0'
}, },
'30%, 60%': { '30%, 60%': {
'background-position': 'calc(100% + var(--shimmer-width)) 0', 'background-position': 'calc(100% + var(--shimmer-width)) 0'
}, }
}, }
}, },
animation: { animation: {
marquee: "marquee var(--duration) linear infinite", marquee: 'marquee var(--duration) linear infinite',
"marquee-vertical": "marquee-vertical var(--duration) linear infinite", 'marquee-vertical': 'marquee-vertical var(--duration) linear infinite',
"accordion-down": "accordion-down 0.2s ease-out", 'accordion-down': 'accordion-down 0.2s ease-out',
"accordion-up": "accordion-up 0.2s ease-out", 'accordion-up': 'accordion-up 0.2s ease-out',
'image-glow': 'image-glow 4100ms 600ms ease-out forwards', 'image-glow': 'image-glow 4100ms 600ms ease-out forwards',
'shimmer': 'shimmer 8s infinite', shimmer: 'shimmer 8s infinite',
"fade-in": "fade-in 1000ms var(--animation-delay, 0ms) ease forwards", 'fade-in': 'fade-in 1000ms var(--animation-delay, 0ms) ease forwards',
"caret-blink": "caret-blink 1.25s ease-out infinite", 'caret-blink': 'caret-blink 1.25s ease-out infinite',
"border-beam": "border-beam calc(var(--duration)*1s) infinite linear", 'border-beam': 'border-beam calc(var(--duration)*1s) infinite linear',
'fade-up': 'fade-up 1000ms var(--animation-delay, 0ms) ease forwards'
"fade-up": "fade-up 1000ms var(--animation-delay, 0ms) ease forwards", }
}, }
},
}, },
plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")], plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
} satisfies Config; } satisfies Config;

193
yarn.lock

@ -1157,6 +1157,16 @@
dependencies: dependencies:
"@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-avatar@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-avatar/-/react-avatar-1.1.1.tgz#5848d2ed5f34d18b36fc7e2d227c41fca8600ea1"
integrity sha512-eoOtThOmxeoizxpX6RiEsQZ2wj5r4+zoeqAwO0cBaFQGjJwIH3dIX0OCxNrCyrrdxG+vBweMETh3VziQG7c1kw==
dependencies:
"@radix-ui/react-context" "1.1.1"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-checkbox@^1.1.1": "@radix-ui/react-checkbox@^1.1.1":
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.1.1.tgz#a559c4303957d797acee99914480b755aa1f27d6" resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.1.1.tgz#a559c4303957d797acee99914480b755aa1f27d6"
@ -1171,6 +1181,20 @@
"@radix-ui/react-use-previous" "1.1.0" "@radix-ui/react-use-previous" "1.1.0"
"@radix-ui/react-use-size" "1.1.0" "@radix-ui/react-use-size" "1.1.0"
"@radix-ui/react-collapsible@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz#1382cc9ec48f8b473c14f3779d317f0cdf6da5e9"
integrity sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-context" "1.1.1"
"@radix-ui/react-id" "1.1.0"
"@radix-ui/react-presence" "1.1.1"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-controllable-state" "1.1.0"
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-collection@1.0.3": "@radix-ui/react-collection@1.0.3":
version "1.0.3" version "1.0.3"
resolved "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz" resolved "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz"
@ -1229,6 +1253,11 @@
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8" resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8"
integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A== integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==
"@radix-ui/react-context@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a"
integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==
"@radix-ui/react-dialog@1.0.5": "@radix-ui/react-dialog@1.0.5":
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz#71657b1b116de6c7a0b03242d7d43e01062c7300" resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz#71657b1b116de6c7a0b03242d7d43e01062c7300"
@ -1250,7 +1279,7 @@
aria-hidden "^1.1.1" aria-hidden "^1.1.1"
react-remove-scroll "2.5.5" react-remove-scroll "2.5.5"
"@radix-ui/react-dialog@^1.0.4", "@radix-ui/react-dialog@^1.1.1": "@radix-ui/react-dialog@^1.0.4":
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz#4906507f7b4ad31e22d7dad69d9330c87c431d44" resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz#4906507f7b4ad31e22d7dad69d9330c87c431d44"
integrity sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg== integrity sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==
@ -1270,6 +1299,26 @@
aria-hidden "^1.1.1" aria-hidden "^1.1.1"
react-remove-scroll "2.5.7" react-remove-scroll "2.5.7"
"@radix-ui/react-dialog@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz#d9345575211d6f2d13e209e84aec9a8584b54d6c"
integrity sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-context" "1.1.1"
"@radix-ui/react-dismissable-layer" "1.1.1"
"@radix-ui/react-focus-guards" "1.1.1"
"@radix-ui/react-focus-scope" "1.1.0"
"@radix-ui/react-id" "1.1.0"
"@radix-ui/react-portal" "1.1.2"
"@radix-ui/react-presence" "1.1.1"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-slot" "1.1.0"
"@radix-ui/react-use-controllable-state" "1.1.0"
aria-hidden "^1.1.1"
react-remove-scroll "2.6.0"
"@radix-ui/react-direction@1.0.1": "@radix-ui/react-direction@1.0.1":
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz" resolved "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz"
@ -1305,16 +1354,27 @@
"@radix-ui/react-use-callback-ref" "1.1.0" "@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-escape-keydown" "1.1.0" "@radix-ui/react-use-escape-keydown" "1.1.0"
"@radix-ui/react-dropdown-menu@^2.1.1": "@radix-ui/react-dismissable-layer@1.1.1":
version "2.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.1.tgz#3dc578488688250dbbe109d9ff2ca28a9bca27ec" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz#cbdcb739c5403382bdde5f9243042ba643883396"
integrity sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ== integrity sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==
dependencies: dependencies:
"@radix-ui/primitive" "1.1.0" "@radix-ui/primitive" "1.1.0"
"@radix-ui/react-compose-refs" "1.1.0" "@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-context" "1.1.0" "@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-escape-keydown" "1.1.0"
"@radix-ui/react-dropdown-menu@^2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.2.tgz#acc49577130e3c875ef0133bd1e271ea3392d924"
integrity sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-context" "1.1.1"
"@radix-ui/react-id" "1.1.0" "@radix-ui/react-id" "1.1.0"
"@radix-ui/react-menu" "2.1.1" "@radix-ui/react-menu" "2.1.2"
"@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-controllable-state" "1.1.0" "@radix-ui/react-use-controllable-state" "1.1.0"
@ -1330,6 +1390,11 @@
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz#8e9abb472a9a394f59a1b45f3dd26cfe3fc6da13" resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz#8e9abb472a9a394f59a1b45f3dd26cfe3fc6da13"
integrity sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw== integrity sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==
"@radix-ui/react-focus-guards@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz#8635edd346304f8b42cae86b05912b61aef27afe"
integrity sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==
"@radix-ui/react-focus-scope@1.0.4": "@radix-ui/react-focus-scope@1.0.4":
version "1.0.4" version "1.0.4"
resolved "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz" resolved "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz"
@ -1440,6 +1505,30 @@
aria-hidden "^1.1.1" aria-hidden "^1.1.1"
react-remove-scroll "2.5.7" react-remove-scroll "2.5.7"
"@radix-ui/react-menu@2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.1.2.tgz#91f6815845a4298dde775563ed2d80b7ad667899"
integrity sha512-lZ0R4qR2Al6fZ4yCCZzu/ReTFrylHFxIqy7OezIpWF4bL0o9biKo0pFIvkaew3TyZ9Fy5gYVrR5zCGZBVbO1zg==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-collection" "1.1.0"
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-context" "1.1.1"
"@radix-ui/react-direction" "1.1.0"
"@radix-ui/react-dismissable-layer" "1.1.1"
"@radix-ui/react-focus-guards" "1.1.1"
"@radix-ui/react-focus-scope" "1.1.0"
"@radix-ui/react-id" "1.1.0"
"@radix-ui/react-popper" "1.2.0"
"@radix-ui/react-portal" "1.1.2"
"@radix-ui/react-presence" "1.1.1"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-roving-focus" "1.1.0"
"@radix-ui/react-slot" "1.1.0"
"@radix-ui/react-use-callback-ref" "1.1.0"
aria-hidden "^1.1.1"
react-remove-scroll "2.6.0"
"@radix-ui/react-menubar@^1.1.1": "@radix-ui/react-menubar@^1.1.1":
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-menubar/-/react-menubar-1.1.1.tgz#e126514cb1c46e0a4f9fba7d016e578cc4e41f22" resolved "https://registry.yarnpkg.com/@radix-ui/react-menubar/-/react-menubar-1.1.1.tgz#e126514cb1c46e0a4f9fba7d016e578cc4e41f22"
@ -1548,6 +1637,14 @@
"@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-layout-effect" "1.1.0" "@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-portal@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.2.tgz#51eb46dae7505074b306ebcb985bf65cc547d74e"
integrity sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==
dependencies:
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-presence@1.0.1": "@radix-ui/react-presence@1.0.1":
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz" resolved "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz"
@ -1565,6 +1662,14 @@
"@radix-ui/react-compose-refs" "1.1.0" "@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-use-layout-effect" "1.1.0" "@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-presence@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz#98aba423dba5e0c687a782c0669dcd99de17f9b1"
integrity sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==
dependencies:
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-primitive@1.0.3": "@radix-ui/react-primitive@1.0.3":
version "1.0.3" version "1.0.3"
resolved "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz" resolved "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz"
@ -1642,13 +1747,12 @@
"@radix-ui/react-use-callback-ref" "1.1.0" "@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-layout-effect" "1.1.0" "@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-separator@^1.0.3": "@radix-ui/react-separator@^1.1.0":
version "1.0.3" version "1.1.0"
resolved "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz" resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.1.0.tgz#ee0f4d86003b0e3ea7bc6ccab01ea0adee32663e"
integrity sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw== integrity sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-primitive" "1.0.3"
"@radix-ui/react-slot@1.0.2": "@radix-ui/react-slot@1.0.2":
version "1.0.2" version "1.0.2"
@ -1692,24 +1796,23 @@
"@radix-ui/react-roving-focus" "1.1.0" "@radix-ui/react-roving-focus" "1.1.0"
"@radix-ui/react-use-controllable-state" "1.1.0" "@radix-ui/react-use-controllable-state" "1.1.0"
"@radix-ui/react-tooltip@^1.0.7": "@radix-ui/react-tooltip@^1.1.3":
version "1.0.7" version "1.1.3"
resolved "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz" resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.3.tgz#4250b14723f2d8477e7a3d0526c169f91d1f2f74"
integrity sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw== integrity sha512-Z4w1FIS0BqVFI2c1jZvb/uDVJijJjJ2ZMuPV81oVgTZ7g3BZxobplnMVvXtFWgtozdvYJ+MFWtwkM5S2HnAong==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@radix-ui/primitive" "1.1.0"
"@radix-ui/primitive" "1.0.1" "@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-compose-refs" "1.0.1" "@radix-ui/react-context" "1.1.1"
"@radix-ui/react-context" "1.0.1" "@radix-ui/react-dismissable-layer" "1.1.1"
"@radix-ui/react-dismissable-layer" "1.0.5" "@radix-ui/react-id" "1.1.0"
"@radix-ui/react-id" "1.0.1" "@radix-ui/react-popper" "1.2.0"
"@radix-ui/react-popper" "1.1.3" "@radix-ui/react-portal" "1.1.2"
"@radix-ui/react-portal" "1.0.4" "@radix-ui/react-presence" "1.1.1"
"@radix-ui/react-presence" "1.0.1" "@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-primitive" "1.0.3" "@radix-ui/react-slot" "1.1.0"
"@radix-ui/react-slot" "1.0.2" "@radix-ui/react-use-controllable-state" "1.1.0"
"@radix-ui/react-use-controllable-state" "1.0.1" "@radix-ui/react-visually-hidden" "1.1.0"
"@radix-ui/react-visually-hidden" "1.0.3"
"@radix-ui/react-use-callback-ref@1.0.1": "@radix-ui/react-use-callback-ref@1.0.1":
version "1.0.1" version "1.0.1"
@ -1815,6 +1918,13 @@
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "1.0.3" "@radix-ui/react-primitive" "1.0.3"
"@radix-ui/react-visually-hidden@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz#ad47a8572580f7034b3807c8e6740cd41038a5a2"
integrity sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==
dependencies:
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/rect@1.0.1": "@radix-ui/rect@1.0.1":
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz" resolved "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz"
@ -2809,7 +2919,7 @@ citty@^0.1.6:
class-variance-authority@^0.7.0: class-variance-authority@^0.7.0:
version "0.7.0" version "0.7.0"
resolved "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz" resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522"
integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A== integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==
dependencies: dependencies:
clsx "2.0.0" clsx "2.0.0"
@ -5010,10 +5120,10 @@ lru-cache@^10.2.0:
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz"
integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==
lucide-react@^0.416.0: lucide-react@^0.454.0:
version "0.416.0" version "0.454.0"
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.416.0.tgz#657da248f9b862703d7d80aafb912e79ad886313" resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.454.0.tgz#a81b9c482018720f07ead0503ae502d94d528444"
integrity sha512-wPWxTzdss1CTz2aqcNWNlbh4YSnH9neJWP3RaeXepxpLCTW+pmu7WcT/wxJe+Q7Y7DqGOxAqakJv0pIK3431Ag== integrity sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==
luxon@~3.4.0: luxon@~3.4.0:
version "3.4.4" version "3.4.4"
@ -6708,7 +6818,7 @@ react-markdown@^9.0.1:
unist-util-visit "^5.0.0" unist-util-visit "^5.0.0"
vfile "^6.0.0" vfile "^6.0.0"
react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.4: react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.4, react-remove-scroll-bar@^2.3.6:
version "2.3.6" version "2.3.6"
resolved "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz" resolved "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz"
integrity sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g== integrity sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==
@ -6738,6 +6848,17 @@ react-remove-scroll@2.5.7:
use-callback-ref "^1.3.0" use-callback-ref "^1.3.0"
use-sidecar "^1.1.2" use-sidecar "^1.1.2"
react-remove-scroll@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz#fb03a0845d7768a4f1519a99fdb84983b793dc07"
integrity sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==
dependencies:
react-remove-scroll-bar "^2.3.6"
react-style-singleton "^2.2.1"
tslib "^2.1.0"
use-callback-ref "^1.3.0"
use-sidecar "^1.1.2"
react-resizable-panels@^2.0.23: react-resizable-panels@^2.0.23:
version "2.0.23" version "2.0.23"
resolved "https://registry.yarnpkg.com/react-resizable-panels/-/react-resizable-panels-2.0.23.tgz#7a4296f23028c32ffcbe8086aa918cd934e7e87f" resolved "https://registry.yarnpkg.com/react-resizable-panels/-/react-resizable-panels-2.0.23.tgz#7a4296f23028c32ffcbe8086aa918cd934e7e87f"