From db992412d010758afbe4c364438768f52aaece7c Mon Sep 17 00:00:00 2001 From: dvelo Date: Fri, 4 Apr 2025 21:10:56 -0500 Subject: [PATCH] feat: clean up code --- apps/www/next.config.mjs | 4 + apps/www/package.json | 2 +- .../modification/custom/[custom-mod]/page.tsx | 103 +++- .../category/[category]/page.tsx | 78 +-- .../file/[filename]/page.tsx | 519 ++---------------- .../sl-modification-frame/files/page.tsx | 202 ++++--- .../www/src/components/feat/footer/footer.tsx | 71 ++- .../custom-files/custom-errors.tsx | 153 ++++++ .../modification/custom-files/custom-lint.tsx | 91 +++ .../custom-files/custom-test-success.tsx | 150 +++++ .../modification/custom-files/custom-test.tsx | 182 ++++++ .../modification-custom-modification-row.tsx} | 49 +- .../feat/server-page/debug/debug-menu.tsx | 336 ++++++------ .../feat/server-page/stats/stats-main-row.tsx | 306 ++++++----- apps/www/src/config/tags.tsx | 3 +- yarn.lock | 2 +- 16 files changed, 1265 insertions(+), 986 deletions(-) create mode 100644 apps/www/src/components/feat/server-list/modification/custom-files/custom-errors.tsx create mode 100644 apps/www/src/components/feat/server-list/modification/custom-files/custom-lint.tsx create mode 100644 apps/www/src/components/feat/server-list/modification/custom-files/custom-test-success.tsx create mode 100644 apps/www/src/components/feat/server-list/modification/custom-files/custom-test.tsx rename apps/www/src/{app/api/trpc/[trpc]/route.ts => components/feat/server-list/modification/modification-custom-modification-row.tsx} (53%) diff --git a/apps/www/next.config.mjs b/apps/www/next.config.mjs index 467a269..b7a7bea 100644 --- a/apps/www/next.config.mjs +++ b/apps/www/next.config.mjs @@ -38,6 +38,10 @@ const nextConfig = { protocol: "https", hostname: "img.clerk.com", }, + { + protocol: "https", + hostname: "avatars.githubusercontent.com" + } ], }, async redirects() { diff --git a/apps/www/package.json b/apps/www/package.json index 022d105..2fa2438 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@babel/parser": "^7.24.7", - "@biomejs/biome": "^1.8.3", + "@biomejs/biome": "^1.9.4", "@clerk/elements": "^0.22.2", "@clerk/nextjs": "^6.9.2", "@emotion/is-prop-valid": "^1.3.0", diff --git a/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/modification/custom/[custom-mod]/page.tsx b/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/modification/custom/[custom-mod]/page.tsx index 68c04ba..5b214c9 100644 --- a/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/modification/custom/[custom-mod]/page.tsx +++ b/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/modification/custom/[custom-mod]/page.tsx @@ -30,8 +30,7 @@ "use client"; -import { ModificationAction } from "@/components/feat/server-list/modification/modification-action"; -import { ClerkCustomActivatedModification } from "@/components/feat/server-list/modification/modification-file-creation-dialog"; +import type { ClerkCustomActivatedModification } from "@/components/feat/server-list/modification/modification-file-creation-dialog"; import { Setting, SettingContent, @@ -40,15 +39,30 @@ import { SettingTitle, } from "@/components/feat/settings/setting"; import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Material } from "@/components/ui/material"; +import { Placeholder } from "@/components/ui/placeholder"; import { Separator } from "@/components/ui/separator"; import { Link } from "@/components/util/link"; import { serverModDB } from "@/config/sl-mod-db"; import { useUser } from "@clerk/nextjs"; -import { ArrowLeft, Filter, SortAsc } from "lucide-react"; +import { + ArrowLeft, + EllipsisVertical, + FileQuestion, + Filter, + SortAsc, + Trash, +} from "lucide-react"; import { useQueryState } from "nuqs"; import { use } from "react"; import Markdown from "react-markdown"; +import { toast } from "sonner"; export default function ModificationPage({ params, @@ -61,13 +75,28 @@ export default function ModificationPage({ defaultValue: "/servers/embedded/sl-modification-frame", }); console.log(mod); - const modObj = ( + const modIndex = ( (user?.unsafeMetadata .activatedModifications as ClerkCustomActivatedModification[]) ?? [] - ).find((c) => c.friendlyName === atob(decodeURIComponent(mod))); + ).findIndex((c) => c.friendlyName === atob(decodeURIComponent(mod))); - if (modObj === undefined) - return <>We couldn't find the modification you were looking for.; + if (modIndex === -1) + return ( +
+ + + + } + /> +
+ ); + + const modObj = ((user?.unsafeMetadata + .activatedModifications as ClerkCustomActivatedModification[]) ?? [])[ + modIndex + ]; return (
@@ -86,13 +115,48 @@ export default function ModificationPage({ This is a custom modification. Enable it! (or not) It's your own! (are you proud?) - +
+ + + + + + + { + const time = Date.now(); + const array = + (user?.unsafeMetadata + .activatedModifications as ClerkCustomActivatedModification[]) ?? + []; + array.splice(modIndex, 1); + await user?.update({ + unsafeMetadata: { + ...user.unsafeMetadata, + activatedModifications: array, + }, + }); + toast.success(`Deleted in ${Date.now() - time}ms`); + }} + > + Delete + + + +
- + @@ -115,6 +179,23 @@ export default function ModificationPage({ + + + + File name + + + + {modObj.originalFileName}.ts + + + +
diff --git a/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/page.tsx b/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/page.tsx index 5910a26..d7f0bc9 100644 --- a/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/page.tsx +++ b/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/page.tsx @@ -28,9 +28,8 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -"use client"; - import { ModificationAction } from "@/components/feat/server-list/modification/modification-action"; +import { ModificationCustomModificationRow } from "@/components/feat/server-list/modification/modification-custom-modification-row"; import { ClerkCustomActivatedModification } from "@/components/feat/server-list/modification/modification-file-creation-dialog"; import { Material } from "@/components/ui/material"; import { Separator } from "@/components/ui/separator"; @@ -43,17 +42,15 @@ import { ArrowLeft, Binary } from "lucide-react"; import { use } from "react"; import Markdown from "react-markdown"; -export default function ServerListCategoryFrame({ +export default async function ServerListCategoryFrame({ params, }: { params: Promise<{ category: string }>; }) { - const { user } = useUser(); - const { category } = use(params); + const { category } = await params; const categoryObj = serverModDB.find( - (c) => c.displayTitle === atob(category), + (c) => c.displayTitle === atob(decodeURIComponent(category)), ); - const router = useRouter(); return (
@@ -67,57 +64,32 @@ export default function ServerListCategoryFrame({ {categoryObj?.entries.map((m) => ( - - router.push( - `/servers/embedded/sl-modification-frame/category/${category}/modification/${btoa(m.name)}?b=${encodeURIComponent(`/servers/embedded/sl-modification-frame/category/${category}`)}`, - ) - } + -
- -
- - {m.name} - -
+
+ +
+ + {m.name} + +
+ ))} - {categoryObj?.__custom && - ( - (user?.unsafeMetadata - .activatedModifications as ClerkCustomActivatedModification[]) ?? - [] - ).map((m) => ( - - router.push( - `/servers/embedded/sl-modification-frame/category/${category}/modification/_custom/${btoa(m.friendlyName)}`, - ) - } - > -
- -
- - {m.friendlyName} - -
- ))} + {categoryObj?.__custom && ( + + )}
diff --git a/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/file/[filename]/page.tsx b/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/file/[filename]/page.tsx index 1a51021..f10c86f 100644 --- a/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/file/[filename]/page.tsx +++ b/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/file/[filename]/page.tsx @@ -2,14 +2,10 @@ import { use, useEffect, useRef, useState } from "react"; import { useUser } from "@clerk/nextjs"; -import type { - ClerkCustomActivatedModification, - ClerkCustomModification, -} from "@/components/feat/server-list/modification/modification-file-creation-dialog"; +import type { ClerkCustomModification } from "@/components/feat/server-list/modification/modification-file-creation-dialog"; import { Link } from "@/components/util/link"; -import { AlertOctagon, ArrowLeft, Check, ExternalLink } from "lucide-react"; +import { ArrowLeft, FileQuestion } from "lucide-react"; import Editor from "@monaco-editor/react"; -import { Button } from "@/components/ui/button"; import { toast } from "sonner"; import * as ts from "typescript"; import useClipboard from "@/lib/useClipboard"; @@ -19,50 +15,18 @@ import { TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; -import type { languages, Uri } from "monaco-editor"; -import { - Drawer, - DrawerContent, - DrawerTitle, - DrawerTrigger, -} from "@/components/ui/drawer"; -import { Alert } from "@/components/ui/alert"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { compressToEncodedURIComponent } from "lz-string"; -import { Geist_Mono } from "next/font/google"; +import type { languages } from "monaco-editor"; import { cn } from "@/lib/utils"; import { debounce } from "lodash"; import { tryCatch } from "@/lib/try-catch"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { Material } from "@/components/ui/material"; -import { - Setting, - SettingContent, - SettingDescription, - SettingMeta, - SettingTitle, -} from "@/components/feat/settings/setting"; -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Input } from "@/components/ui/input"; -import { DialogTrigger } from "@/components/ui/dialog"; -import { useRouter } from "@/lib/useRouter"; +import { Placeholder } from "@/components/ui/placeholder"; +import { CustomErrors } from "@/components/feat/server-list/modification/custom-files/custom-errors"; +import { CustomLint } from "@/components/feat/server-list/modification/custom-files/custom-lint"; +import { CustomTest } from "@/components/feat/server-list/modification/custom-files/custom-test"; + +export type MonacoRefType = typeof import( + "monaco-editor/esm/vs/editor/editor.api" +); const typeDefs = `// Hi :) how'd you get here? // Here, in return I'll provide you with a random number: ${Math.ceil(Math.random() * 100)} @@ -122,8 +86,6 @@ export const transpileTypeScript = (code: string) => { } }; -const geistMono = Geist_Mono({ subsets: ["latin"] }); - export default function CustomFilePage({ params, }: { @@ -138,43 +100,28 @@ export default function CustomFilePage({ const [syntaxErrors, setSyntaxErrors] = useState< languages.typescript.Diagnostic[] | null >(null); - const [testMode, setTestMode] = useState(""); - const router = useRouter(); const file = ( (user?.unsafeMetadata.customFiles as Array) ?? [] ).findIndex((c) => c.name === filename); if (file === -1) { - return <>Bruh.; + return ( +
+ + + + } + /> +
+ ); } - const validateCode = (code: string) => { - if (!monacoRef.current) return; - - monacoRef.current.languages.typescript - .getTypeScriptWorker() - .then((worker) => { - worker( - monacoRef.current?.Uri.parse(`file:///${filename}.ts`) as Uri, - ).then((client) => { - client - .getSemanticDiagnostics( - ( - monacoRef.current?.Uri.parse(`file:///${filename}.ts`) as Uri - ).toString(), - ) - .then((diags) => { - setSyntaxErrors(diags); - }); - }); - }); - }; - const fileContents = ((user?.unsafeMetadata .customFiles as Array) ?? [])[file].contents; const [value, setValue] = useState(fileContents); const clipboard = useClipboard(); - validateCode(value); const saveFile = async () => { const metadata = @@ -194,40 +141,6 @@ export default function CustomFilePage({ }); }; - const lintFile = async () => { - toast.info("Transpiling TypeScript..."); - const { error, data: transpiledCode } = await tryCatch( - (async () => transpileTypeScript(value))(), - ); - if (error) { - toast.error("Failed to transpile TypeScript! Error: " + error.message); - return; - } - const startTime = Date.now(); - if (transpiledCode === null) { - toast.error("Cannot continue."); - return; - } - toast.info("Generating function..."); - const functionBody = transpiledCode.match( - /function\s+filter\s*\([^)]*\)\s*\{([\s\S]*)\}/, - )?.[1]; - const { error: filterErr, data: filterFunc } = await tryCatch( - (async () => new Function("data", functionBody as string))(), - ); - if (filterErr) { - toast.error(`Failed to generate function! Error: ${filterErr.message}`); - return; - } - if (typeof filterFunc === "function") { - toast.success("Linted in " + (Date.now() - startTime) + "ms"); - setSuccessfullyLinted(true); - } else { - toast.error("Code doesn't have a 'filter' function. Cannot be tested."); - toast.error(typeof filterFunc); - } - }; - const debouncedSave = debounce(async () => { const { error } = await tryCatch(saveFile()); if (error) @@ -236,9 +149,9 @@ export default function CustomFilePage({ ); }, 300); + // biome-ignore lint: L useEffect(() => { setSuccessfullyLinted(false); - validateCode(value); debouncedSave(); }, [value]); @@ -253,350 +166,33 @@ export default function CustomFilePage({ {syntaxErrors !== null && syntaxErrors.length !== 0 && ( - - - - - - Type Errors -
- {syntaxErrors.map((c, i) => ( - - {c.messageText.toString()}{" "} - - - - (TS{typeof c !== "string" && c.code}) - - - - - - typescript.tv - - - - ts-error-translator - - - - - - ))} -
-
-
+ )} - + {syntaxErrors !== null && syntaxErrors.length !== 0 - ? "You must have no type errors in the editor to lint, you have " + - syntaxErrors.length + - " error(s)." + ? `You must have no type errors in the editor to lint, you have ${syntaxErrors.length} error(s).` : "Check for possible runtime errors."} - {(() => { - const [open, setOpen] = useState(false); - const [filterEnabled, setFilterEnabled] = useState(true); - const [sortEnabled, setSortEnabled] = useState(true); - const [success, setSuccess] = useState(false); - const [fileName, setFileName] = useState(""); - - useEffect(() => { - setFilterEnabled(true); - setSortEnabled(true); - setTestMode(""); - (async () => { - const transpiledValue = transpileTypeScript(value); - const functionBody = transpiledValue - ?.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( - (async () => - new Function( - "server", - `${functionBody} - return filter(server)`, - ))(), - ); - const { error: sortErr, data: sortFunc } = await tryCatch( - (async () => - new Function( - "serverA", - "serverB", - `${functionBody} - return sort(serverA, serverB)`, - ))(), - ); - - if (filterErr) setFilterEnabled(false); - if (sortErr) setSortEnabled(false); - - try { - filterFunc?.({}); - } catch (e) { - if ( - String(e).startsWith( - "ReferenceError: filter is not defined", - ) - ) { - setFilterEnabled(false); - } - } - try { - sortFunc?.({}, {}); - } catch (e) { - if ( - String(e).startsWith( - "ReferenceError: sort is not defined", - ) - ) { - setSortEnabled(false); - } - } - })(); - }, [open]); - - return ( - <> - - - -

- You can run an interactive server-list environment - with actual online servers to test your modifications. -

- - - - - - Function to test - - You can pick to either test a sorting system - or a filter. - - - - - - - - - {success && ( - <> -

- You can now activate this custom modification. - Please note that the filter and sort versions of - your modifications will be different, and the one - used will be selected based on what type you - tested on. -

- {( - (user?.unsafeMetadata - .activatedModifications as ClerkCustomActivatedModification[]) ?? - [] - ).find((c) => c.originalFileName === filename && c.testMode === testMode) !== - undefined && ( - - This modification was already activated! Hitting - activate here will just overwrite the contents - and the new friendly name. - - )} - - - - - - Name - - Set a friendly name for your modification. - - - - setFileName(c.target.value) - } - /> - - - - - - - - )} -
-
- - ); - })()} +
{successfullyLinted @@ -641,23 +237,6 @@ export default function CustomFilePage({ libUri, ); - // Add actions - [ - { - id: "manually-save-file", - label: "MHSF: Manually Save File", - run: () => { - saveFile(); - toast.success("Manually saved file!"); - }, - }, - { - id: "lint-file", - label: "MHSF: Lint File", - run: lintFile, - }, - ].forEach((e) => editor.addAction(e)); - // Create a model for the libUri file if (!monaco.editor.getModel(monaco.Uri.parse(libUri))) { monaco.editor.createModel( @@ -756,23 +335,3 @@ export async function findSupportedOperations( return returnValue; } - -function guidGenerator() { - const S4 = () => { - return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); - }; - return ( - S4() + - S4() + - "-" + - S4() + - "-" + - S4() + - "-" + - S4() + - "-" + - S4() + - S4() + - S4() - ); -} diff --git a/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/files/page.tsx b/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/files/page.tsx index 49fd4ea..2918326 100644 --- a/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/files/page.tsx +++ b/apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/files/page.tsx @@ -33,106 +33,126 @@ import { ClerkCustomModification } from "@/components/feat/server-list/modification/modification-file-creation-dialog"; import { Button } from "@/components/ui/button"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Material } from "@/components/ui/material"; import { Placeholder } from "@/components/ui/placeholder"; import { Link } from "@/components/util/link"; import { useUser } from "@clerk/nextjs"; import { - ArrowLeft, - Braces, - EllipsisVertical, - FileCode, - Filter, - Pencil, - SortAsc, - Trash, + ArrowLeft, + Braces, + EllipsisVertical, + FileCode, + Filter, + Pencil, + SortAsc, + Trash, } from "lucide-react"; -import { use } from "react"; +import { use, useEffect, useState } from "react"; import { toast } from "sonner"; import { findSupportedOperations } from "../file/[filename]/page"; export default function ServerListModificationFrame() { - const { user } = useUser(); - const files = - (user?.unsafeMetadata.customFiles as Array) ?? []; - const operations = use((async () => await Promise.all(files.map(async (c) => await findSupportedOperations(c.contents))))()) - console.log(operations) - return ( -
-

- - - - Files -

- - {files.length === 0 && ( - } - title="We couldn't find any files" - description="Try creating a filter!" - /> - )} - {files.map((c, i) => ( - - - - {operations[i].filter && } - {operations[i].sort && } - {c.name}.ts - - - - - - - - { - e.stopPropagation(); - const startTime = Date.now(); - files.splice(i, 1); - await user?.update({ - unsafeMetadata: { - ...user.unsafeMetadata, - customFiles: files, - }, - }); - toast.success( - "Deleted file in " + (Date.now() - startTime) + "ms" - ); - }} - > - Delete - - - Rename - - - - - - ))} - -
- ); + const { user } = useUser(); + const files = + (user?.unsafeMetadata.customFiles as Array) ?? []; + const operations = usePlatforms(files); + + + return ( +
+

+ + + + Files +

+ + {files.length === 0 && ( + } + title="We couldn't find any files" + description="Try creating a filter!" + /> + )} + {files.map((c, i) => ( + + + + {operations[i].filter && } + {operations[i].sort && } + {c.name}.ts + + + + + + + + + { + e.stopPropagation(); + const startTime = Date.now(); + files.splice(i, 1); + await user?.update({ + unsafeMetadata: { + ...user.unsafeMetadata, + customFiles: files, + }, + }); + toast.success( + "Deleted file in " + (Date.now() - startTime) + "ms", + ); + }} + > + Delete + + + Rename + + + + + + ))} + +
+ ); +} + +function usePlatforms(files: Array) { + const [result, setResult] = useState< + Array<{ sort: boolean; filter: boolean }> + >([]); + + useEffect(() => { + (async () => { + setResult( + await Promise.all( + files.map(async (c) => await findSupportedOperations(c.contents)), + ), + ); + })(); + }, [files]); + + return result; } diff --git a/apps/www/src/components/feat/footer/footer.tsx b/apps/www/src/components/feat/footer/footer.tsx index 6a66e3a..962cfe7 100644 --- a/apps/www/src/components/feat/footer/footer.tsx +++ b/apps/www/src/components/feat/footer/footer.tsx @@ -1,12 +1,15 @@ -import { BrandingGenericIcon } from "../icons/branding-icons"; +import { BrandingGenericIcon, Discord } from "../icons/branding-icons"; import { Link } from "../../util/link"; import { FooterStatus } from "./status"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; +import { Button } from "@/components/ui/button"; +import Github from "@/components/ui/github"; +import Image from "next/image" export function Footer() { return (