mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-08 00:44:59 -05:00
fix: biome, test mode selection
This commit is contained in:
parent
2078e5fba1
commit
bddf5f1528
@ -1,5 +1,5 @@
|
|||||||
import { ServerProvider } from "@/components/feat/server-page/server-provider";
|
import { ServerProvider } from "@/components/feat/server-page/server-provider";
|
||||||
import { ServerResponse } from "@/lib/types/mh-server";
|
import type { ServerResponse } from "@/lib/types/mh-server";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
|
|||||||
@ -46,7 +46,6 @@ import {
|
|||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
Braces,
|
Braces,
|
||||||
EllipsisVertical,
|
EllipsisVertical,
|
||||||
File,
|
|
||||||
FileCode,
|
FileCode,
|
||||||
Pencil,
|
Pencil,
|
||||||
Trash,
|
Trash,
|
||||||
@ -76,7 +75,7 @@ export default function ServerListModificationFrame() {
|
|||||||
{files.map((c, i) => (
|
{files.map((c, i) => (
|
||||||
<Link
|
<Link
|
||||||
href={`/servers/embedded/sl-modification-frame/file/${c.name}`}
|
href={`/servers/embedded/sl-modification-frame/file/${c.name}`}
|
||||||
className="w-full py-1 px-2 rounded-xl flex items-center gap-1 justify-between hover:bg-slate-100"
|
className="w-full py-1 px-2 rounded-xl flex items-center gap-1 justify-between hover:bg-slate-100 dark:hover:bg-zinc-700/30"
|
||||||
key={c.name}
|
key={c.name}
|
||||||
>
|
>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
@ -88,7 +87,7 @@ export default function ServerListModificationFrame() {
|
|||||||
<DropdownMenuTrigger>
|
<DropdownMenuTrigger>
|
||||||
<Button
|
<Button
|
||||||
variant="tertiary"
|
variant="tertiary"
|
||||||
className="flex items-center justify-center hover:bg-slate-200"
|
className="flex items-center justify-center hover:bg-slate-200 dark:hover:bg-zinc-700/60"
|
||||||
size="square-sm"
|
size="square-sm"
|
||||||
>
|
>
|
||||||
<EllipsisVertical
|
<EllipsisVertical
|
||||||
|
|||||||
@ -225,6 +225,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes loading-shimmer {
|
||||||
|
0% {
|
||||||
|
background-position: 125% 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: -150% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-shimmer {
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
animation-duration: 2.5s;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-name: loading-shimmer;
|
||||||
|
background: var(--color-muted-foreground)
|
||||||
|
linear-gradient(
|
||||||
|
90deg,
|
||||||
|
var(--color-muted-foreground) 0%,
|
||||||
|
var(--color-shadcn-primary) 50%,
|
||||||
|
var(--color-muted-foreground) 100%
|
||||||
|
);
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 50% 200%;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
||||||
so we've added these compatibility styles to make sure everything still
|
so we've added these compatibility styles to make sure everything still
|
||||||
|
|||||||
@ -32,11 +32,11 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
||||||
import { ModificationFrame } from "./modification-frame";
|
import { ModificationFrame } from "./modification-frame";
|
||||||
|
|
||||||
export function ModificationButton() {
|
export function ModificationButton({disabled}: {disabled?: boolean}) {
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<Button>Filters & Sorting</Button>
|
<Button disabled={disabled}>Filters & Sorting</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent className="p-0 h-[600px] w-[1000px] !max-w-[800px] overflow-x-hidden">
|
<DialogContent className="p-0 h-[600px] w-[1000px] !max-w-[800px] overflow-x-hidden">
|
||||||
|
|||||||
@ -36,13 +36,14 @@ import { Separator } from "@/components/ui/separator";
|
|||||||
import { Statistics } from "./statistics";
|
import { Statistics } from "./statistics";
|
||||||
import InfiniteScroll from "react-infinite-scroll-component";
|
import InfiniteScroll from "react-infinite-scroll-component";
|
||||||
import { useInfiniteScrolling } from "@/lib/hooks/use-infinite-scrolling";
|
import { useInfiniteScrolling } from "@/lib/hooks/use-infinite-scrolling";
|
||||||
import { useMHSFServer } from "@/lib/hooks/use-mhsf-multiple";
|
|
||||||
import { ModificationButton } from "./modification/modification-button";
|
import { ModificationButton } from "./modification/modification-button";
|
||||||
import { useFilters } from "@/lib/hooks/use-filters";
|
import { useFilters } from "@/lib/hooks/use-filters";
|
||||||
|
import { ServerTestModeSelector } from "./server-test-mode-selector";
|
||||||
|
|
||||||
export function ServerList() {
|
export function ServerList() {
|
||||||
const { servers, loading, serverCount, playerCount } = useServers();
|
const { servers, loading, serverCount, playerCount } = useServers();
|
||||||
const { filteredData } = useFilters(servers);
|
const { filteredData, testModeEnabled, testModeLoading, testModeStatus } =
|
||||||
|
useFilters(servers);
|
||||||
const { itemsLength, fetchMoreData, hasMoreData, data } =
|
const { itemsLength, fetchMoreData, hasMoreData, data } =
|
||||||
useInfiniteScrolling(filteredData);
|
useInfiniteScrolling(filteredData);
|
||||||
|
|
||||||
@ -67,7 +68,14 @@ export function ServerList() {
|
|||||||
<h1 className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl">
|
<h1 className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl">
|
||||||
Servers
|
Servers
|
||||||
</h1>
|
</h1>
|
||||||
<ModificationButton />
|
<div className="flex items-center">
|
||||||
|
<ModificationButton disabled={testModeEnabled} />
|
||||||
|
<ServerTestModeSelector
|
||||||
|
testModeStatus={testModeStatus}
|
||||||
|
testModeEnabled={testModeEnabled}
|
||||||
|
testModeLoading={testModeLoading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
dataLength={itemsLength}
|
dataLength={itemsLength}
|
||||||
next={fetchMoreData}
|
next={fetchMoreData}
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { AnimatedText } from "@/components/ui/animated-text";
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
|
import { useQueryState } from "nuqs";
|
||||||
|
|
||||||
|
export function ServerTestModeSelector({
|
||||||
|
testModeStatus,
|
||||||
|
testModeLoading,
|
||||||
|
testModeEnabled,
|
||||||
|
}: {
|
||||||
|
testModeStatus: string;
|
||||||
|
testModeLoading: boolean;
|
||||||
|
testModeEnabled: boolean;
|
||||||
|
}) {
|
||||||
|
const [tm] = useQueryState("tm", { defaultValue: "" });
|
||||||
|
|
||||||
|
if (testModeEnabled || tm !== "")
|
||||||
|
return (
|
||||||
|
<div className="pl-5 flex items-center gap-1">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<span className="relative flex size-2.5 pt-[1px] items-center cursor-pointer">
|
||||||
|
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-orange-400 opacity-75" />
|
||||||
|
<span className="relative inline-flex size-2.5 rounded-full bg-orange-500" />
|
||||||
|
</span></TooltipTrigger>
|
||||||
|
<TooltipContent>Test mode was enabled.</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
<AnimatedText
|
||||||
|
className="text-muted-foreground top-[2.5px] left-[6px] min-w-[70vw]"
|
||||||
|
text={testModeStatus}
|
||||||
|
glimmer
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@ -6,9 +6,10 @@ import { cn } from "@/lib/utils";
|
|||||||
interface AnimatedTextProps {
|
interface AnimatedTextProps {
|
||||||
text: string;
|
text: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
glimmer?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AnimatedText({ text, className }: AnimatedTextProps) {
|
export function AnimatedText({ text, className, glimmer }: AnimatedTextProps) {
|
||||||
const [currentText, setCurrentText] = useState(text);
|
const [currentText, setCurrentText] = useState(text);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -24,7 +25,9 @@ export function AnimatedText({ text, className }: AnimatedTextProps) {
|
|||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
exit={{ y: -20, opacity: 0 }}
|
exit={{ y: -20, opacity: 0 }}
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
className={cn("absolute left-0", className)}
|
className={cn("absolute left-0", className, {
|
||||||
|
"loading-shimmer": glimmer,
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
{currentText}
|
{currentText}
|
||||||
</motion.span>
|
</motion.span>
|
||||||
|
|||||||
@ -39,6 +39,8 @@ export function useFilters(data: OnlineServer[]) {
|
|||||||
const [filteredData, setFilteredData] = useState<OnlineServer[]>(data);
|
const [filteredData, setFilteredData] = useState<OnlineServer[]>(data);
|
||||||
const [t] = useQueryState("tm");
|
const [t] = useQueryState("tm");
|
||||||
const [testModeEnabled, setTestModeEnabled] = useState(false);
|
const [testModeEnabled, setTestModeEnabled] = useState(false);
|
||||||
|
const [testModeStatus, setTestModeStatus] = useState("Haven't connected thread yet (if stuck, select the other tab, and come back)");
|
||||||
|
const [testModeLoading, setTestModeLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (filteredData.length === 0) setFilteredData(data);
|
if (filteredData.length === 0) setFilteredData(data);
|
||||||
@ -54,68 +56,91 @@ export function useFilters(data: OnlineServer[]) {
|
|||||||
setTestModeEnabled(true);
|
setTestModeEnabled(true);
|
||||||
const code = atob(t);
|
const code = atob(t);
|
||||||
(async () => {
|
(async () => {
|
||||||
toast.info("Transpiling TypeScript...");
|
setTestModeStatus("Transpiling TypeScript...");
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const { error, data: transpiledCode } = await tryCatch(
|
const { error, data: transpiledCode } = await tryCatch(
|
||||||
(async () => transpileTypeScript(code))()
|
(async () => transpileTypeScript(code))()
|
||||||
);
|
);
|
||||||
if (error) {
|
if (error) {
|
||||||
toast.error(
|
setTestModeStatus(
|
||||||
"Failed to transpile TypeScript! Error: " + error.message
|
"Failed to transpile TypeScript! Error: " + error.message
|
||||||
);
|
);
|
||||||
|
setTestModeLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (transpiledCode === null) {
|
if (transpiledCode === null) {
|
||||||
toast.error("Cannot continue.");
|
setTestModeStatus("Cannot continue.");
|
||||||
|
setTestModeLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(
|
console.log(
|
||||||
"[MHSF Filters] Transpiled TypeScript:",
|
"[MHSF Filters] Transpiled TypeScript:",
|
||||||
transpiledCode ?? ""
|
transpiledCode ?? ""
|
||||||
);
|
);
|
||||||
toast.info("Generating function...");
|
setTestModeStatus("Generating function...");
|
||||||
const functionBody = transpiledCode.match(
|
if (
|
||||||
/function\s+filter\s*\([^)]*\)\s*\{([\s\S]*)\}/
|
!transpiledCode.includes("export default") &&
|
||||||
)?.[1];
|
!transpiledCode.includes("export")
|
||||||
|
) {
|
||||||
|
setTestModeStatus(
|
||||||
|
"Transpiled code does not contain any export statements."
|
||||||
|
);
|
||||||
|
setTestModeLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const functionBody = transpiledCode
|
||||||
|
.replace(/export default(?!.*[;])/g, "") // Avoid replacing if followed by a semicolon
|
||||||
|
.replace(/export(?!.*[;])/g, ""); // Avoid replacing if followed by a semicolon
|
||||||
const { error: filterErr, data: filterFunc } = await tryCatch(
|
const { error: filterErr, data: filterFunc } = await tryCatch(
|
||||||
(async () => new Function("server", functionBody as string))()
|
(async () =>
|
||||||
|
new Function(
|
||||||
|
"server",
|
||||||
|
`${functionBody}
|
||||||
|
|
||||||
|
return filter(server)`
|
||||||
|
))()
|
||||||
);
|
);
|
||||||
if (filterErr) {
|
if (filterErr) {
|
||||||
toast.error(
|
setTestModeStatus(
|
||||||
`Failed to generate function! Error: ${filterErr.message}`
|
`Failed to generate function! Error: ${filterErr.message}`
|
||||||
);
|
);
|
||||||
|
setTestModeLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof filterFunc === "function") {
|
if (typeof filterFunc === "function") {
|
||||||
toast.success("Compiled in " + (Date.now() - startTime) + "ms");
|
setTestModeStatus(
|
||||||
|
"Compiled in " + (Date.now() - startTime) + "ms"
|
||||||
|
);
|
||||||
toast.promise(
|
toast.promise(
|
||||||
async () => {
|
async () => {
|
||||||
let newServers = [];
|
let newServers = [];
|
||||||
newServers = data.filter((c) => filterFunc(c));
|
newServers = data.filter((c) => filterFunc(c));
|
||||||
toast.info(
|
setTestModeStatus(
|
||||||
"Server count " + data.length + " -> " + newServers.length
|
"Server count " + data.length + " -> " + newServers.length
|
||||||
);
|
);
|
||||||
setFilteredData(() => [...newServers]);
|
setFilteredData(() => [...newServers]);
|
||||||
|
setTestModeLoading(false);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loading: "Manipulating data...",
|
loading: "Manipulating data...",
|
||||||
success: "Manipulated data; test mode finished!",
|
success: "Manipulated data; test mode finished!",
|
||||||
error: (e) =>
|
error: (e) =>
|
||||||
`Error while manipulating data; go back to your editor and run again. ${es}`,
|
`Error while manipulating data; go back to your editor and run again. ${e}`,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
toast.error(
|
setTestModeStatus(
|
||||||
"Code doesn't have a 'filter' function. Cannot be tested."
|
"Code doesn't have a 'filter' function. Cannot be tested."
|
||||||
);
|
);
|
||||||
toast.error(typeof filterFunc);
|
setTestModeLoading(false);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [t, data]);
|
}, [t, data]);
|
||||||
|
|
||||||
console.log(filteredData);
|
console.log(filteredData, testModeStatus);
|
||||||
|
|
||||||
return { filteredData, testModeEnabled };
|
return { filteredData, testModeEnabled, testModeLoading, testModeStatus };
|
||||||
}
|
}
|
||||||
|
|
||||||
12
biome.json
Normal file
12
biome.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
||||||
|
"linter": {
|
||||||
|
"rules": {
|
||||||
|
"style": {
|
||||||
|
"useImportType": {
|
||||||
|
"level": "warn"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user