fix: misc

This commit is contained in:
dvelo 2025-05-16 19:44:00 -05:00
parent 2a58192a14
commit 88e703625c
6 changed files with 223 additions and 127 deletions

@ -150,7 +150,7 @@ export const CodeHighlight = ({
const language = match ? match[1] : undefined; const language = match ? match[1] : undefined;
return ( return (
<ShikiHighlighter language={language} theme="poimandres" {...props}> <ShikiHighlighter language={language?.toLocaleLowerCase()} theme="poimandres" {...props}>
{String(children)} {String(children)}
</ShikiHighlighter> </ShikiHighlighter>
); );

@ -1,13 +1,13 @@
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Material } from "@/components/ui/material"; import { Material } from "@/components/ui/material";
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup, SelectGroup,
SelectItem, SelectItem,
SelectLabel, SelectLabel,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { useMHSFServer } from "@/lib/hooks/use-mhsf-server"; import { useMHSFServer } from "@/lib/hooks/use-mhsf-server";
@ -17,78 +17,82 @@ import { Moon, Sun } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
export function ServerColorModeBox({ export function ServerColorModeBox({
serverData, serverData,
minehutData, minehutData,
}: { }: {
serverData: ReturnType<typeof useMHSFServer>; serverData: ReturnType<typeof useMHSFServer>;
minehutData: ServerResponse; minehutData: ServerResponse;
}) { }) {
const [colorModeEnabled, setColorModeEnabled] = useState(false); const [colorModeEnabled, setColorModeEnabled] = useState(false);
const [colorMode, setColorMode] = useState("light"); const [colorMode, setColorMode] = useState("light");
useEffect(() => {
setColorModeEnabled(serverData.server?.customizationData.colorMode !== undefined);
setColorMode(serverData.server?.customizationData.colorMode ?? "light");
}, [serverData])
useEffect(() => { useEffect(() => {
if (colorMode === "idc") { setColorModeEnabled(
setColorModeEnabled(false); serverData.server?.customizationData.colorMode !== null,
setColorMode("light"); );
} setColorMode(serverData.server?.customizationData.colorMode ?? "light");
if (colorMode === "dark") }, [serverData]);
window.dispatchEvent(new Event("force-dark-mode"));
if (colorMode === "light") useEffect(() => {
window.dispatchEvent(new Event("force-light-mode")); if (colorMode === "idc") {
}, [colorMode]); setColorModeEnabled(false);
setColorMode("light");
}
if (colorModeEnabled) {
if (colorMode === "dark")
window.dispatchEvent(new Event("force-dark-mode"));
useEffect(() => { if (colorMode === "light")
update(); window.dispatchEvent(new Event("force-light-mode"));
}, [colorMode, colorModeEnabled]); } else window.dispatchEvent(new Event("force-no-mode"));
}, [colorMode, colorModeEnabled]);
const update = debounce(async () => { useEffect(() => {
await fetch( update();
`/api/v1/server/get/${minehutData._id}/settings/change-color-mode${colorModeEnabled !== false ? `?colorMode=${colorMode}` : ""}` }, [colorMode, colorModeEnabled]);
);
}, 500);
return ( const update = debounce(async () => {
<Material className="flex justify-between items-center p-2 mt-2"> await fetch(
<div className="flex items-center font-bold gap-4"> `/api/v1/server/get/${minehutData._id}/settings/change-color-mode${colorModeEnabled !== false ? `?colorMode=${colorMode}` : ""}`,
<Switch );
checked={colorModeEnabled} }, 500);
onCheckedChange={setColorModeEnabled}
/>{" "}
Enforce color mode
</div>
<Select return (
disabled={!colorModeEnabled} <Material className="flex justify-between items-center p-2 mt-2">
onValueChange={setColorMode} <div className="flex items-center font-bold gap-4">
value={colorMode} <Switch
> checked={colorModeEnabled}
<SelectTrigger className="w-[180px] disabled:hidden"> onCheckedChange={setColorModeEnabled}
<SelectValue placeholder="Select mode" /> />{" "}
</SelectTrigger> Enforce color mode
<SelectContent> </div>
<SelectGroup>
<SelectItem value="dark"> <Select
<div className="flex items-center gap-2"> disabled={!colorModeEnabled}
<Moon size={16} /> onValueChange={setColorMode}
Dark value={colorMode}
</div> >
</SelectItem> <SelectTrigger className="w-[180px] disabled:hidden">
<SelectItem value="light"> <SelectValue placeholder="Select mode" />
<div className="flex items-center gap-2"> </SelectTrigger>
<Sun size={16} /> <SelectContent>
Light <SelectGroup>
</div> <SelectItem value="dark">
</SelectItem> <div className="flex items-center gap-2">
<SelectItem value="idc">I couldn't care less</SelectItem> <Moon size={16} />
</SelectGroup> Dark
</SelectContent> </div>
</Select> </SelectItem>
</Material> <SelectItem value="light">
); <div className="flex items-center gap-2">
<Sun size={16} />
Light
</div>
</SelectItem>
<SelectItem value="idc">I couldn't care less</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</Material>
);
} }

@ -8,61 +8,71 @@ import { ServerPageButtons } from "./server-page-buttons";
import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server"; import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { useEffect } from "react"; import { useEffect } from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
export function ServerMainPage({ export function ServerMainPage({
server, server,
mhsfData, mhsfData,
}: { }: {
server: ServerResponse; server: ServerResponse;
mhsfData: ReturnType<typeof useMHSFServer>; mhsfData: ReturnType<typeof useMHSFServer>;
}) { }) {
useEffect(() => { useEffect(() => {
if (mhsfData.server?.customizationData.colorMode !== undefined) { if (mhsfData.server?.customizationData.colorMode !== null) {
if (mhsfData.server?.customizationData.colorMode === "dark") if (mhsfData.server?.customizationData.colorMode === "dark")
window.dispatchEvent(new Event("force-dark-mode")); window.dispatchEvent(new Event("force-dark-mode"));
if (mhsfData.server?.customizationData.colorMode === "light") if (mhsfData.server?.customizationData.colorMode === "light")
window.dispatchEvent(new Event("force-light-mode")); window.dispatchEvent(new Event("force-light-mode"));
} }
}); });
return ( return (
<div <div
className={cn( className={cn(
"xl:px-[100px]", "xl:px-[100px]",
mhsfData.server?.customizationData.banner === undefined mhsfData.server?.customizationData.banner === undefined
? "pt-[150px]" ? "pt-[150px]"
: "pt-[300px]" : "pt-[300px]",
)} )}
> >
{mhsfData.server?.customizationData.banner && ( {mhsfData.server?.customizationData.banner && (
<img <img
src={mhsfData.server?.customizationData.banner} src={mhsfData.server?.customizationData.banner}
alt="User provided banner for server" alt="User provided banner for server"
className="rounded align-middle block ml-auto mr-auto absolute left-0 z-0 w-full object-fill" className="rounded align-middle block ml-auto mr-auto absolute left-0 z-0 w-full object-fill"
style={{ style={{
maskImage: "linear-gradient(to top, transparent, black)", maskImage: "linear-gradient(to top, transparent, black)",
top: "0", top: "0",
}} }}
/> />
)} )}
<span className="flex items-center gap-2 w-full relative"> <span className="flex items-center gap-2 w-full relative">
<div className="bg-secondary p-4 rounded-lg lg:ml-4"> <div className="bg-secondary p-4 rounded-lg lg:ml-4">
<IconDisplay server={server} /> <IconDisplay server={server} />
</div> </div>
<p className="w-full"> <p className="w-full">
<div className="lg:flex justify-between w-full"> <div className="lg:flex justify-between w-full">
<h1 className="text-2xl font-bold">{server.name}</h1> <h1 className="text-2xl font-bold flex items-center gap-1 ml-2">
<span> <Avatar className="h-[32px] w-[32px]">
<ServerPageButtons server={server} mhsfData={mhsfData} /> <AvatarImage
</span> src={mhsfData.server?.customizationData.userProfilePicture ?? ""}
</div> alt="Server Owner Image"
<span className="flex items-center gap-2 flex-wrap"> />
<ServerPageTags server={server} className="mt-1" /> <AvatarFallback>{server.name[0]}</AvatarFallback>
</span> </Avatar>
</p> {server.name}
</span> </h1>
<Separator className="my-6" /> <span>
<ServerRows server={server} mhsfData={mhsfData} /> <ServerPageButtons server={server} mhsfData={mhsfData} />
</div> </span>
); </div>
<span className="flex items-center gap-2 flex-wrap">
<ServerPageTags server={server} className="mt-1" />
</span>
</p>
</span>
<Separator className="my-6" />
<ServerRows server={server} mhsfData={mhsfData} />
</div>
);
} }

@ -0,0 +1,79 @@
/*
* 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) 2025 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 * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar"
import { cn } from "@/lib/utils"
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className
)}
{...props}
/>
))
Avatar.displayName = AvatarPrimitive.Root.displayName
const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
))
AvatarImage.displayName = AvatarPrimitive.Image.displayName
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className
)}
{...props}
/>
))
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
export { Avatar, AvatarImage, AvatarFallback }

@ -49,6 +49,9 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
window.addEventListener("force-light-mode", () => { window.addEventListener("force-light-mode", () => {
setForcedTheme('light'); setForcedTheme('light');
}); });
window.addEventListener("force-no-mode", () => {
setForcedTheme(undefined);
});
}); });
React.useEffect(() => { React.useEffect(() => {

@ -36,7 +36,7 @@ export type ActualCustomization = {
/** @version 1 @deprecated Use `colorMode` instead */ /** @version 1 @deprecated Use `colorMode` instead */
colorScheme: string | undefined; colorScheme: string | undefined;
/** @version 2 */ /** @version 2 */
colorMode: "dark" | "light" | undefined; colorMode: "dark" | "light" | null;
customizationVersion: number | undefined; customizationVersion: number | undefined;
} & ( } & (
| { | {