mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 14:04:59 -05:00
feat: add new embeds
This commit is contained in:
parent
bdf5b9b52e
commit
ddea87ccf8
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Ignore everything for IntelliJ except for project essential code-styles
|
||||
*
|
||||
|
||||
!copyright/*
|
||||
!codeStyles/*
|
||||
!.gitignore
|
||||
|
||||
!*/
|
||||
7
.idea/codeStyles/Project.xml
generated
Normal file
7
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
6
.vscode/.gitignore
vendored
Normal file
6
.vscode/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# Ignore everything for VSCode except for project essential code-styles
|
||||
*
|
||||
|
||||
!extensions.json
|
||||
!settings.json
|
||||
!.gitignore
|
||||
5
.vscode/extensions.json
vendored
Normal file
5
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"sarfrajansari.copyright-header-injector"
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"copyright-header-injector.copyrightText": "/*\n * MHSF, Minehut Server List\n * All external content is rather licensed under the ECA Agreement\n * located here: https://mhsf.app/docs/legal/external-content-agreement\n *\n * All code under MHSF is licensed under the MIT License\n * by open source contributors\n *\n * Copyright (c) 2024 dvelo\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */"
|
||||
}
|
||||
@ -25,6 +25,7 @@
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-menubar": "^1.1.1",
|
||||
"@radix-ui/react-primitive": "^2.0.0",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-switch": "^1.1.0",
|
||||
"@unocss/eslint-plugin": "^0.61.5",
|
||||
"@unocss/postcss": "^0.61.5",
|
||||
@ -105,7 +106,7 @@
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-resizable-panels": "^2.0.23",
|
||||
"recharts": "^2.12.7",
|
||||
"shiki": "^1.22.2",
|
||||
"shiki": "^1.23.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5",
|
||||
"vaul": "^0.9.1",
|
||||
|
||||
39
src/app/(embeds)/embed/[server]/page.tsx
Normal file
39
src/app/(embeds)/embed/[server]/page.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/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 Embed from "@/components/feat/Embed";
|
||||
|
||||
export default function EmbedPage({
|
||||
params,
|
||||
}: {
|
||||
params: { server: string };
|
||||
}) {
|
||||
return <Embed params={params} />;
|
||||
}
|
||||
58
src/app/(embeds)/layout.tsx
Normal file
58
src/app/(embeds)/layout.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/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 { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import "../globals.css";
|
||||
import { ThemeProvider } from "@/components/ThemeProvider";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const searchParams = useSearchParams();
|
||||
const search = searchParams?.get("theme") || "light";
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
disableTransitionOnChange
|
||||
forcedTheme={search}
|
||||
>
|
||||
<TooltipProvider>{children}</TooltipProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@ -33,11 +33,8 @@ 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 };
|
||||
@ -45,7 +42,7 @@ type Props = {
|
||||
|
||||
export async function generateMetadata(
|
||||
{ params }: Props,
|
||||
parent: ResolvingMetadata,
|
||||
parent: ResolvingMetadata
|
||||
): Promise<Metadata> {
|
||||
// read route params
|
||||
const { server } = params;
|
||||
@ -135,12 +132,6 @@ export default function ServerPage({ params }: { params: { server: string } }) {
|
||||
<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} />
|
||||
|
||||
@ -36,9 +36,8 @@ import {
|
||||
MinehutIcon,
|
||||
getIndexFromRarity,
|
||||
getMinehutIcons,
|
||||
rarityIndex,
|
||||
} from "@/lib/types/server-icon";
|
||||
import { Copy, Info } from "lucide-react";
|
||||
import { Copy, ExternalLink, Info } from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useEffect, useState } from "react";
|
||||
import FadeIn from "react-fade-in/lib/FadeIn";
|
||||
@ -55,6 +54,8 @@ import {
|
||||
CardTitle,
|
||||
} from "./ui/card";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
|
||||
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from "./ui/drawer";
|
||||
import EmbedSelector from "./feat/EmbedSelector";
|
||||
|
||||
export default function AfterServerView({ server }: { server: string }) {
|
||||
const [description, setDescription] = useState("");
|
||||
@ -64,11 +65,12 @@ export default function AfterServerView({ server }: { server: string }) {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [view, setView] = useState(
|
||||
description !== "" || discord !== "" ? "desc" : "extra",
|
||||
description !== "" || discord !== "" ? "desc" : "extra"
|
||||
);
|
||||
const [serverObject, setServerObject] = useState<ServerResponse | undefined>(
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const [embedOpened, setEmbedOpened] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@ -82,7 +84,7 @@ export default function AfterServerView({ server }: { server: string }) {
|
||||
});
|
||||
}
|
||||
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))
|
||||
);
|
||||
getMinehutIcons().then((i) => {
|
||||
setIcons(i);
|
||||
@ -94,9 +96,17 @@ export default function AfterServerView({ server }: { server: string }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Drawer open={embedOpened} onOpenChange={setEmbedOpened}>
|
||||
<DrawerContent className="max-w-md w-full mx-auto rounded-t-[10px]">
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Embed Creator</DrawerTitle>
|
||||
</DrawerHeader>
|
||||
<EmbedSelector server={server} />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
<FadeIn>
|
||||
<div className="grid sm:grid-cols-6 h-full pl-4 pr-4 ">
|
||||
<div className="ml-5 mb-2 flex items-center sm:hidden">
|
||||
<div className="ml-5 mb-2 flex items-center sm:hidden overflow-auto w-[calc(100vw-5rem)]">
|
||||
{(description != "" || discord != "") && (
|
||||
<Button
|
||||
variant={view == "desc" ? undefined : "ghost"}
|
||||
@ -125,6 +135,10 @@ export default function AfterServerView({ server }: { server: string }) {
|
||||
>
|
||||
Purchased Icons
|
||||
</Button>
|
||||
<Button variant="ghost" onClick={() => setEmbedOpened(true)}>
|
||||
Embed Creator
|
||||
<ExternalLink className="h-[1.2rem] w-[1.2rem] ml-1" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="max-sm:hidden">
|
||||
<div className="grid">
|
||||
@ -154,6 +168,10 @@ export default function AfterServerView({ server }: { server: string }) {
|
||||
>
|
||||
Purchased Icons
|
||||
</Button>
|
||||
<Button variant="ghost" onClick={() => setEmbedOpened(true)}>
|
||||
Embed Creator
|
||||
<ExternalLink className="h-[1.2rem] w-[1.2rem] ml-1" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -349,11 +367,11 @@ export default function AfterServerView({ server }: { server: string }) {
|
||||
JSON.stringify({
|
||||
minehut: serverObject,
|
||||
mhsf: mhsf.getMHSF(),
|
||||
}),
|
||||
})
|
||||
);
|
||||
} catch {
|
||||
toast.error(
|
||||
"Clipboard is inaccessible. Cannot copy",
|
||||
"Clipboard is inaccessible. Cannot copy"
|
||||
);
|
||||
}
|
||||
toast.success(
|
||||
@ -366,7 +384,7 @@ export default function AfterServerView({ server }: { server: string }) {
|
||||
}).substring(0, 36)}
|
||||
...
|
||||
</code>
|
||||
</div>,
|
||||
</div>
|
||||
);
|
||||
setTimeout(() => setCopied(false), 1000);
|
||||
}}
|
||||
@ -438,9 +456,7 @@ export default function AfterServerView({ server }: { server: string }) {
|
||||
className="pt-4"
|
||||
style={{
|
||||
color: getIndexFromRarity(
|
||||
icons
|
||||
?.find((c) => c._id === icon)
|
||||
?.rank.toLowerCase(),
|
||||
icons?.find((c) => c._id === icon)?.rank.toLowerCase()
|
||||
).text,
|
||||
}}
|
||||
>
|
||||
@ -457,7 +473,7 @@ export default function AfterServerView({ server }: { server: string }) {
|
||||
backgroundColor: getIndexFromRarity(
|
||||
icons
|
||||
?.find((c) => c._id === icon)
|
||||
?.rank.toLowerCase(),
|
||||
?.rank.toLowerCase()
|
||||
).bg,
|
||||
}}
|
||||
>
|
||||
|
||||
@ -430,7 +430,6 @@ export function ServerCommandBar() {
|
||||
)}
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
{owned && (
|
||||
<h2 className="flex items-center text-muted-foreground">
|
||||
<CheckIcon />
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Spinner } from "./ui/spinner";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@ -108,7 +107,7 @@ export default function ServerView(props: { server: string }) {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{single.grabOnline() == undefined && (
|
||||
{single.grabOnline() == undefined && !single.grabOffline()?.online && (
|
||||
<div className="grid pl-4 pr-4">
|
||||
<div
|
||||
className=" rounded p-2"
|
||||
@ -130,7 +129,8 @@ export default function ServerView(props: { server: string }) {
|
||||
<Card className="sm:col-span-2">
|
||||
<BetterHeader>
|
||||
<CardTitle className="flex items-center">
|
||||
{single.grabOnline() == undefined ? (
|
||||
{single.grabOnline() == undefined &&
|
||||
!single.grabOffline()?.online ? (
|
||||
<div
|
||||
className="items-center mr-1"
|
||||
style={{
|
||||
|
||||
168
src/components/feat/Embed.tsx
Normal file
168
src/components/feat/Embed.tsx
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/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 IconDisplay from "@/components/IconDisplay";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import type { ServerResponse } from "@/lib/types/mh-server";
|
||||
import { Copy, ExternalLink, ServerCrash } from "lucide-react";
|
||||
import { notFound, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button } from "../ui/button";
|
||||
import { CheckmarkIcon } from "react-hot-toast";
|
||||
import useClipboard from "@/lib/useClipboard";
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from "../ui/tooltip";
|
||||
|
||||
export default function Embed({ params }: { params: { server: string } }) {
|
||||
const [serverFound, setServerFound] = useState(true);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [serverObject, setServerObject] = useState<ServerResponse | null>(null);
|
||||
const searchParams = useSearchParams();
|
||||
const staticMode = searchParams?.get("static") === "true";
|
||||
const noShowBranding = searchParams?.get("branding") === "false";
|
||||
const clipboard = useClipboard();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const serverFoundResponse = await fetch(
|
||||
"https://api.minehut.com/server/" + params.server + "?byName=true"
|
||||
);
|
||||
const stream = await serverFoundResponse.json();
|
||||
|
||||
if (stream.server == null) setServerFound(false);
|
||||
else setServerObject(stream.server);
|
||||
setLoading(false);
|
||||
})();
|
||||
}, [params]);
|
||||
|
||||
if (loading) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
if (!serverFound) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded w-[390px] h-[145px] bg-muted">
|
||||
<div
|
||||
className={
|
||||
"flex items-center text-sm cursor-pointer border-b p-2" +
|
||||
(staticMode ? "" : " group")
|
||||
}
|
||||
onClick={() =>
|
||||
window.open("/server/" + params.server, "_blank")?.focus()
|
||||
}
|
||||
>
|
||||
<ServerCrash
|
||||
size={16}
|
||||
className="group-hover:text-white p-[4px] group-hover:p-[3px] w-[24px] h-[24px] transition-all bg-gradient-to-r group-hover:from-blue-600 group-hover:to-purple-500 group-hover:rounded"
|
||||
/>
|
||||
<span className="transition-colors ml-2 group-hover:bg-clip-text group-hover:text-transparent bg-gradient-to-r group-hover:from-blue-600 group-hover:to-purple-500">
|
||||
Powered by MHSF
|
||||
</span>
|
||||
</div>
|
||||
<div className="px-4 pt-2 flex items-center group overflow-hidden">
|
||||
<div className={staticMode ? "block" : "group-hover:block hidden"}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="mb-1"
|
||||
onClick={() => {
|
||||
setCopied(true);
|
||||
clipboard.writeText(params.server + ".mhsf.minehut.gg");
|
||||
setTimeout(() => setCopied(false), 1000);
|
||||
}}
|
||||
>
|
||||
{copied ? <CheckmarkIcon /> : <Copy size={16} />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Copy this server IP</TooltipContent>
|
||||
</Tooltip>{" "}
|
||||
<br />
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
window.open("/server/" + params.server, "_blank")?.focus();
|
||||
}}
|
||||
>
|
||||
<ExternalLink size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
<IconDisplay
|
||||
server={serverObject}
|
||||
className={
|
||||
"flex items-center mr-2" +
|
||||
(staticMode ? " mb-1 ml-1" : " group-hover:mb-1 group-hover:ml-1")
|
||||
}
|
||||
/>
|
||||
<div className={"block" + (staticMode ? " mb-1" : " group-hover:mb-1")}>
|
||||
<strong className="text-lg">{params.server}</strong>{" "}
|
||||
{!noShowBranding && <Badge variant="blue">on Minehut</Badge>}
|
||||
<br />
|
||||
<span className="text-sm">Joined {serverObject?.joins} times</span>
|
||||
<br />
|
||||
{serverObject?.online && (
|
||||
<span className="flex items-center">
|
||||
{serverObject.playerCount === 0 ? (
|
||||
<div
|
||||
className="items-center border"
|
||||
style={{
|
||||
width: ".5rem",
|
||||
height: ".5rem",
|
||||
borderRadius: "9999px",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="items-center"
|
||||
style={{
|
||||
backgroundColor: "#0cce6b",
|
||||
width: ".5rem",
|
||||
height: ".5rem",
|
||||
borderRadius: "9999px",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className="text-sm ml-1">
|
||||
{serverObject.playerCount} player(s) online
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
306
src/components/feat/EmbedSelector.tsx
Normal file
306
src/components/feat/EmbedSelector.tsx
Normal file
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/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 { TabsContent } from "@radix-ui/react-tabs";
|
||||
import { Button } from "../ui/button";
|
||||
import { DrawerFooter, DrawerTrigger } from "../ui/drawer";
|
||||
import { Tabs, TabsList, TabsTrigger } from "../ui/tabs";
|
||||
import { useEffect, useState } from "react";
|
||||
import { codeToHtml } from "shiki";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Asterisk, Copy } from "lucide-react";
|
||||
import useClipboard from "@/lib/useClipboard";
|
||||
import toast from "react-hot-toast";
|
||||
import { Checkbox } from "../ui/checkbox";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "../ui/select";
|
||||
|
||||
export default function EmbedSelector({ server }: { server: string }) {
|
||||
const { theme, systemTheme, resolvedTheme } = useTheme();
|
||||
|
||||
const [embedTheme, setEmbedTheme] = useState("");
|
||||
const [embedStatic, setEmbedStatic] = useState(false);
|
||||
const [highlightedHtml, setHighlightedHtml] = useState("");
|
||||
const [highlightedJsx, setHighlightedJsx] = useState("");
|
||||
const [selectedCodeType, setSelectedCodeType] = useState("jsx");
|
||||
const [noMinehutBranding, setNoMinehutBranding] = useState(false);
|
||||
const clipboard = useClipboard();
|
||||
const [url, setURL] = useState(`https://mhsf.app/embed/${server}?`);
|
||||
const [jsxCode, setJsxCode] = useState(`<iframe
|
||||
src="${url}"
|
||||
width={390}
|
||||
height={145}
|
||||
style={{ borderRadius: 0.25 }}
|
||||
allow="clipboard-write"
|
||||
frameBorder={0}
|
||||
sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
|
||||
/>`);
|
||||
const [htmlCode, setHtmlCode] = useState(`<iframe
|
||||
src="${url}"
|
||||
width="390"
|
||||
height="145"
|
||||
style="border-radius: 0.25rem;"
|
||||
allow="clipboard-write"
|
||||
frameborder="0"
|
||||
sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
|
||||
></iframe>`);
|
||||
|
||||
useEffect(() => {
|
||||
setHtmlCode(`<iframe
|
||||
src="${url}"
|
||||
width="390"
|
||||
height="145"
|
||||
style="border-radius: 0.25rem;"
|
||||
allow="clipboard-write"
|
||||
frameborder="0"
|
||||
sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
|
||||
></iframe>`);
|
||||
setJsxCode(`<iframe
|
||||
src="${url}"
|
||||
width={390}
|
||||
height={145}
|
||||
style={{ borderRadius: 0.25 }}
|
||||
allow="clipboard-write"
|
||||
frameBorder={0}
|
||||
sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
|
||||
/>`);
|
||||
|
||||
const currentTheme = theme === "system" ? systemTheme : theme;
|
||||
const selectedTheme =
|
||||
currentTheme === "dark" ? "vitesse-dark" : "vitesse-light";
|
||||
|
||||
async function highlightCode() {
|
||||
const jsx = await codeToHtml(jsxCode, {
|
||||
lang: "jsx",
|
||||
theme: selectedTheme,
|
||||
});
|
||||
const html = await codeToHtml(htmlCode, {
|
||||
lang: "html",
|
||||
theme: selectedTheme,
|
||||
});
|
||||
setHighlightedHtml(html);
|
||||
setHighlightedJsx(jsx);
|
||||
}
|
||||
|
||||
highlightCode();
|
||||
}, [
|
||||
theme,
|
||||
systemTheme,
|
||||
jsxCode,
|
||||
htmlCode,
|
||||
embedStatic,
|
||||
noMinehutBranding,
|
||||
url,
|
||||
]);
|
||||
|
||||
const renderCode = (code: string, highlighted: string) => {
|
||||
if (highlighted) {
|
||||
return (
|
||||
<div
|
||||
className="h-full overflow-auto bg-background font-mono text-xs [&>pre]:h-full [&>pre]:!bg-transparent [&>pre]:p-4 [&_code]:break-all"
|
||||
dangerouslySetInnerHTML={{ __html: highlighted }}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<pre className="h-full overflow-auto break-all bg-background p-4 font-mono text-xs text-foreground">
|
||||
{code}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="p-4">
|
||||
<div className="px-2 pb-8">
|
||||
<div className="items-top flex space-x-2">
|
||||
<Checkbox
|
||||
id="static"
|
||||
checked={embedStatic}
|
||||
onCheckedChange={(c) => {
|
||||
setEmbedStatic(c == "indeterminate" ? true : c);
|
||||
setURL(
|
||||
`https://mhsf.app/embed/${server}?${c ? "&static=true" : ""}${
|
||||
noMinehutBranding ? "&branding=false" : ""
|
||||
}&theme=${embedTheme}`
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className="grid gap-1.5 leading-none">
|
||||
<label
|
||||
htmlFor="static"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Make embed static
|
||||
</label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Interactions with the embed take less resources but will be less
|
||||
interactive.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div className="items-top flex space-x-2">
|
||||
<Checkbox
|
||||
id="static"
|
||||
checked={noMinehutBranding}
|
||||
onCheckedChange={(c) => {
|
||||
setNoMinehutBranding(c == "indeterminate" ? true : c);
|
||||
setURL(
|
||||
`https://mhsf.app/embed/${server}?${embedStatic ? "&static=true" : ""}${
|
||||
c ? "&branding=false" : ""
|
||||
}&theme=${embedTheme}`
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className="grid gap-1.5 leading-none">
|
||||
<label
|
||||
htmlFor="static"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Remove Minehut branding
|
||||
</label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Enabling this will remove the "on Minehut" tag on the embed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<label
|
||||
htmlFor="theme"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Theme
|
||||
</label>
|
||||
<Select
|
||||
name="theme"
|
||||
value={embedTheme}
|
||||
onValueChange={(c) => {
|
||||
setEmbedTheme(c);
|
||||
setURL(
|
||||
`https://mhsf.app/embed/${server}?${embedStatic ? "&static=true" : ""}${
|
||||
noMinehutBranding ? "&branding=false" : ""
|
||||
}&theme=${c}`
|
||||
);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Light" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="light">Light</SelectItem>
|
||||
<SelectItem value="dark">Dark</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs defaultValue="preview" className="relative mr-auto w-full">
|
||||
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
|
||||
<TabsTrigger
|
||||
value="preview"
|
||||
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
Preview
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="code"
|
||||
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
Code
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="preview">
|
||||
<iframe
|
||||
src={`/embed/${server}?${embedStatic ? "&static=true" : ""}${
|
||||
noMinehutBranding ? "&branding=false" : ""
|
||||
}&theme=${embedTheme}`}
|
||||
width={390}
|
||||
height={145}
|
||||
className="justify-center m-1"
|
||||
style={{ borderRadius: "0.25rem" }}
|
||||
allow="clipboard-write"
|
||||
frameBorder={0}
|
||||
sandbox="allow-forms allow-scripts"
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="code">
|
||||
<div className="bg-secondary h-[43px] px-3 py-1 rounded-b">
|
||||
<div className="w-[130px] grid grid-cols-2">
|
||||
<Button
|
||||
size="icon"
|
||||
className="h-8 w-16 justify-end"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
if (selectedCodeType === "jsx") setSelectedCodeType("html");
|
||||
else setSelectedCodeType("jsx");
|
||||
}}
|
||||
>
|
||||
<Asterisk size={16} className="mr-1" />
|
||||
{selectedCodeType === "jsx" ? <>JSX</> : <>HTML</>}
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
className="h-8 w-8 justify-end"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
clipboard.writeText(
|
||||
selectedCodeType === "jsx" ? jsxCode : htmlCode
|
||||
);
|
||||
toast.success("Copied!");
|
||||
}}
|
||||
>
|
||||
<Copy size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{renderCode(
|
||||
selectedCodeType === "jsx" ? jsxCode : htmlCode,
|
||||
selectedCodeType === "jsx" ? highlightedJsx : highlightedHtml
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
<DrawerFooter>
|
||||
<DrawerTrigger asChild>
|
||||
<Button>Close</Button>
|
||||
</DrawerTrigger>
|
||||
</DrawerFooter>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -32,7 +32,7 @@
|
||||
import { useState } from "react";
|
||||
import { Tabs, TabsList, TabsTrigger } from "../ui/tabs";
|
||||
import { useRouter } from "@/lib/useRouter";
|
||||
import { Database, Home, Paintbrush } from "lucide-react";
|
||||
import { CornerDownLeft, Database, Home, Paintbrush } from "lucide-react";
|
||||
|
||||
export default function TabServer({
|
||||
server,
|
||||
@ -45,7 +45,7 @@ export default function TabServer({
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className="w-full flex justify-center">
|
||||
<div className="w-full px-4">
|
||||
<Tabs
|
||||
value={tab}
|
||||
onValueChange={(tac) => {
|
||||
@ -53,23 +53,40 @@ export default function TabServer({
|
||||
if (tac == "customize") router.push(`/server/${server}/customize`);
|
||||
if (tac == "statistics") router.push(`/server/${server}/statistics`);
|
||||
if (tac == "general") router.push(`/server/${server}`);
|
||||
if (tac == "server-list") router.push("/");
|
||||
}}
|
||||
className="sm:w-[500px] max-sm:w-[200px]"
|
||||
>
|
||||
<TabsList className="grid w-full grid-cols-3 max-sm:min-h-[50px]">
|
||||
<TabsTrigger value="general" className="">
|
||||
<TabsList className="border-b bg-transparent p-0 rounded-none w-full justify-start">
|
||||
<TabsTrigger
|
||||
value="general"
|
||||
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
{" "}
|
||||
<div className="max-sm:hidden">General Information</div>
|
||||
<Home className="sm:hidden" size={18} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="statistics">
|
||||
<TabsTrigger
|
||||
value="statistics"
|
||||
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
<div className="max-sm:hidden">Statistics</div>
|
||||
<Database className="sm:hidden" size={18} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="customize">
|
||||
<TabsTrigger
|
||||
value="customize"
|
||||
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
<div className="max-sm:hidden">Customization</div>
|
||||
<Paintbrush className="sm:hidden" size={18} />
|
||||
</TabsTrigger>
|
||||
|
||||
<TabsTrigger
|
||||
value="server-list"
|
||||
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
<CornerDownLeft size={16} className="mr-2" />
|
||||
<div className="max-sm:hidden">Back to server list</div>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
158
src/components/ui/select.tsx
Normal file
158
src/components/ui/select.tsx
Normal file
@ -0,0 +1,158 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "@radix-ui/react-icons"
|
||||
|
||||
const Select = SelectPrimitive.Root
|
||||
|
||||
const SelectGroup = SelectPrimitive.Group
|
||||
|
||||
const SelectValue = SelectPrimitive.Value
|
||||
|
||||
const SelectTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDownIcon className="h-4 w-4 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
))
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||
|
||||
const SelectScrollUpButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUpIcon className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
))
|
||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
||||
|
||||
const SelectScrollDownButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
))
|
||||
SelectScrollDownButton.displayName =
|
||||
SelectPrimitive.ScrollDownButton.displayName
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover 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",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
))
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName
|
||||
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn("px-2 py-1.5 text-sm font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
||||
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
))
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName
|
||||
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectGroup,
|
||||
SelectValue,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectLabel,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
SelectScrollUpButton,
|
||||
SelectScrollDownButton,
|
||||
}
|
||||
@ -73,6 +73,7 @@ export const allFolders: DocsFolder[] = [
|
||||
name: "Legal",
|
||||
docs: [
|
||||
{ title: "ECA Agreement", url: "/docs/legal/external-content-agreement" },
|
||||
{ title: "Email List", url: "/docs/legal/email-list" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@ -41,7 +41,10 @@ const User = ({ user }: { user: string }) => (
|
||||
const FeatureList = ({
|
||||
features,
|
||||
title,
|
||||
}: { features: (string | ReactNode)[]; title: ReactNode }) => {
|
||||
}: {
|
||||
features: (string | ReactNode)[];
|
||||
title: ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<ul>
|
||||
{title}
|
||||
@ -54,6 +57,25 @@ const FeatureList = ({
|
||||
|
||||
export const version = "1.4.0";
|
||||
export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
|
||||
{
|
||||
id: "r9swempc7kaqd2j84nutv5",
|
||||
name: "v1.5.0",
|
||||
changelog: (
|
||||
<FeatureList
|
||||
features={[
|
||||
"New embeds",
|
||||
"More mobile friendly elements",
|
||||
"Better tabs in the server",
|
||||
"Fixed issue where some servers due to their age were not loading",
|
||||
]}
|
||||
title={
|
||||
<strong className="flex items-center">
|
||||
Version 1.5.0 (November 16th 2024)
|
||||
</strong>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "ywvhtcs4k9rqjfp57x",
|
||||
name: "v1.4.5",
|
||||
|
||||
@ -60,6 +60,9 @@ export default class ServerSingle {
|
||||
if (this.online == true && skipOnline != true) {
|
||||
fetch("https://api.minehut.com/servers").then((l) =>
|
||||
l.json().then((o) => {
|
||||
if (o.servers.find((j: OnlineServer) => j.name == this.name) == undefined) {
|
||||
g(true);
|
||||
}
|
||||
o.servers.forEach((j: OnlineServer) => {
|
||||
if (j.name == this.name) {
|
||||
this.onlineObj = j;
|
||||
|
||||
118
yarn.lock
118
yarn.lock
@ -1747,6 +1747,33 @@
|
||||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
|
||||
"@radix-ui/react-select@^2.1.2":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.1.2.tgz#2346e118966db793940f6a866fd4cc5db2cc275e"
|
||||
integrity sha512-rZJtWmorC7dFRi0owDmoijm6nSJH1tVw64QGiNIZ9PNLyBDtG+iAq+XGsya052At4BfarzY/Dhv9wrrUr6IMZA==
|
||||
dependencies:
|
||||
"@radix-ui/number" "1.1.0"
|
||||
"@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-primitive" "2.0.0"
|
||||
"@radix-ui/react-slot" "1.1.0"
|
||||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
"@radix-ui/react-use-previous" "1.1.0"
|
||||
"@radix-ui/react-visually-hidden" "1.1.0"
|
||||
aria-hidden "^1.1.1"
|
||||
react-remove-scroll "2.6.0"
|
||||
|
||||
"@radix-ui/react-separator@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.1.0.tgz#ee0f4d86003b0e3ea7bc6ccab01ea0adee32663e"
|
||||
@ -1969,39 +1996,39 @@
|
||||
resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.3.tgz#0c102aa2ec5b34f806e9bc8625fc6a5e1d0a0c6a"
|
||||
integrity sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==
|
||||
|
||||
"@shikijs/core@1.22.2":
|
||||
version "1.22.2"
|
||||
resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.22.2.tgz#9c22bd4cc8a4d6c062461cfd35e1faa6c617ca25"
|
||||
integrity sha512-bvIQcd8BEeR1yFvOYv6HDiyta2FFVePbzeowf5pPS1avczrPK+cjmaxxh0nx5QzbON7+Sv0sQfQVciO7bN72sg==
|
||||
"@shikijs/core@1.23.0":
|
||||
version "1.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.23.0.tgz#6504882c7cafc1176b2443e87609b68fbdf10096"
|
||||
integrity sha512-J4Fo22oBlfRHAXec+1AEzcowv+Qdf4ZQkuP/X/UHYH9+KA9LvyFXSXyS+HxuBRFfon+u7bsmKdRBjoZlbDVRkQ==
|
||||
dependencies:
|
||||
"@shikijs/engine-javascript" "1.22.2"
|
||||
"@shikijs/engine-oniguruma" "1.22.2"
|
||||
"@shikijs/types" "1.22.2"
|
||||
"@shikijs/engine-javascript" "1.23.0"
|
||||
"@shikijs/engine-oniguruma" "1.23.0"
|
||||
"@shikijs/types" "1.23.0"
|
||||
"@shikijs/vscode-textmate" "^9.3.0"
|
||||
"@types/hast" "^3.0.4"
|
||||
hast-util-to-html "^9.0.3"
|
||||
|
||||
"@shikijs/engine-javascript@1.22.2":
|
||||
version "1.22.2"
|
||||
resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-1.22.2.tgz#62e90dbd2ed1d78b972ad7d0a1f8ffaaf5e43279"
|
||||
integrity sha512-iOvql09ql6m+3d1vtvP8fLCVCK7BQD1pJFmHIECsujB0V32BJ0Ab6hxk1ewVSMFA58FI0pR2Had9BKZdyQrxTw==
|
||||
"@shikijs/engine-javascript@1.23.0":
|
||||
version "1.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-1.23.0.tgz#51728dd9d68e4ddc123734f816874aded38a1f11"
|
||||
integrity sha512-CcrppseWShG+8Efp1iil9divltuXVdCaU4iu+CKvzTGZO5RmXyAiSx668M7VbX8+s/vt1ZKu75Vn/jWi8O3G/Q==
|
||||
dependencies:
|
||||
"@shikijs/types" "1.22.2"
|
||||
"@shikijs/types" "1.23.0"
|
||||
"@shikijs/vscode-textmate" "^9.3.0"
|
||||
oniguruma-to-js "0.4.3"
|
||||
oniguruma-to-es "0.1.2"
|
||||
|
||||
"@shikijs/engine-oniguruma@1.22.2":
|
||||
version "1.22.2"
|
||||
resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.2.tgz#b12a44e3faf486e19fbcf8952f4b56b9b9b8d9b8"
|
||||
integrity sha512-GIZPAGzQOy56mGvWMoZRPggn0dTlBf1gutV5TdceLCZlFNqWmuc7u+CzD0Gd9vQUTgLbrt0KLzz6FNprqYAxlA==
|
||||
"@shikijs/engine-oniguruma@1.23.0":
|
||||
version "1.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-1.23.0.tgz#c32610bdfe0850e54b10d9e1f5a689f63e681c91"
|
||||
integrity sha512-gS8bZLqVvmZXX+E5JUMJICsBp+kx6gj79MH/UEpKHKIqnUzppgbmEn6zLa6mB5D+sHse2gFei3YYJxQe1EzZXQ==
|
||||
dependencies:
|
||||
"@shikijs/types" "1.22.2"
|
||||
"@shikijs/types" "1.23.0"
|
||||
"@shikijs/vscode-textmate" "^9.3.0"
|
||||
|
||||
"@shikijs/types@1.22.2":
|
||||
version "1.22.2"
|
||||
resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-1.22.2.tgz#695a283f19963fe0638fc2646862ba5cfc4623a8"
|
||||
integrity sha512-NCWDa6LGZqTuzjsGfXOBWfjS/fDIbDdmVDug+7ykVe1IKT4c1gakrvlfFYp5NhAXH/lyqLM8wsAPo5wNy73Feg==
|
||||
"@shikijs/types@1.23.0":
|
||||
version "1.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-1.23.0.tgz#860635725176d8b0cd07cda418fb319fc7a068da"
|
||||
integrity sha512-HiwzsihRao+IbPk7FER/EQT/D0dEEK3n5LAtHDzL5iRT+JMblA7y9uitUnjEnHeLkKigNM+ZplrP7MuEyyc5kA==
|
||||
dependencies:
|
||||
"@shikijs/vscode-textmate" "^9.3.0"
|
||||
"@types/hast" "^3.0.4"
|
||||
@ -3460,6 +3487,11 @@ eastasianwidth@^0.2.0:
|
||||
resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
|
||||
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
|
||||
|
||||
emoji-regex-xs@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz#e8af22e5d9dbd7f7f22d280af3d19d2aab5b0724"
|
||||
integrity sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz"
|
||||
@ -6506,12 +6538,14 @@ onetime@^6.0.0:
|
||||
dependencies:
|
||||
mimic-fn "^4.0.0"
|
||||
|
||||
oniguruma-to-js@0.4.3:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz#8d899714c21f5c7d59a3c0008ca50e848086d740"
|
||||
integrity sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==
|
||||
oniguruma-to-es@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-0.1.2.tgz#157a34f2c6a469c053a5deecf3065f4163279cd4"
|
||||
integrity sha512-sBYKVJlIMB0WPO+tSu/NNB1ytSFeHyyJZ3Ayxfx3f/QUuXu0lvZk0VB4K7npmdlHSC0ldqanzh/sUSlAbgCTfw==
|
||||
dependencies:
|
||||
regex "^4.3.2"
|
||||
emoji-regex-xs "^1.0.0"
|
||||
regex "^4.4.0"
|
||||
regex-recursion "^4.1.0"
|
||||
|
||||
oo-ascii-tree@^1.84.0:
|
||||
version "1.102.0"
|
||||
@ -7028,7 +7062,19 @@ regenerator-runtime@^0.14.0:
|
||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz"
|
||||
integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
|
||||
|
||||
regex@^4.3.2:
|
||||
regex-recursion@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/regex-recursion/-/regex-recursion-4.2.1.tgz#024ee28593b8158e568307b99bf1b7a3d5ea31e9"
|
||||
integrity sha512-QHNZyZAeKdndD1G3bKAbBEKOSSK4KOHQrAJ01N1LJeb0SoH4DJIeFhp0uUpETgONifS4+P3sOgoA1dhzgrQvhA==
|
||||
dependencies:
|
||||
regex-utilities "^2.3.0"
|
||||
|
||||
regex-utilities@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/regex-utilities/-/regex-utilities-2.3.0.tgz#87163512a15dce2908cf079c8960d5158ff43280"
|
||||
integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==
|
||||
|
||||
regex@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/regex/-/regex-4.4.0.tgz#cb731e2819f230fad69089e1bd854fef7569e90a"
|
||||
integrity sha512-uCUSuobNVeqUupowbdZub6ggI5/JZkYyJdDogddJr60L764oxC2pMZov1fQ3wM9bdyzUILDG+Sqx6NAKAz9rKQ==
|
||||
@ -7314,15 +7360,15 @@ shebang-regex@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz"
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
shiki@^1.22.2:
|
||||
version "1.22.2"
|
||||
resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.22.2.tgz#ed109a3d0850504ad5a1edf8496470a2121c5b7b"
|
||||
integrity sha512-3IZau0NdGKXhH2bBlUk4w1IHNxPh6A5B2sUpyY+8utLu2j/h1QpFkAaUA1bAMxOWWGtTWcAh531vnS4NJKS/lA==
|
||||
shiki@^1.23.0:
|
||||
version "1.23.0"
|
||||
resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.23.0.tgz#83e6a2b71d0faf9fa1679b045266ba5de711b4c6"
|
||||
integrity sha512-xfdu9DqPkIpExH29cmiTlgo0/jBki5la1Tkfhsv+Wu5TT3APLNHslR1acxuKJOCWqVdSc+pIbs/2ozjVRGppdg==
|
||||
dependencies:
|
||||
"@shikijs/core" "1.22.2"
|
||||
"@shikijs/engine-javascript" "1.22.2"
|
||||
"@shikijs/engine-oniguruma" "1.22.2"
|
||||
"@shikijs/types" "1.22.2"
|
||||
"@shikijs/core" "1.23.0"
|
||||
"@shikijs/engine-javascript" "1.23.0"
|
||||
"@shikijs/engine-oniguruma" "1.23.0"
|
||||
"@shikijs/types" "1.23.0"
|
||||
"@shikijs/vscode-textmate" "^9.3.0"
|
||||
"@types/hast" "^3.0.4"
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user