diff --git a/app/api/proxy/route.ts b/app/api/proxy/route.ts
index 51c4e2a..42ea64b 100644
--- a/app/api/proxy/route.ts
+++ b/app/api/proxy/route.ts
@@ -1,10 +1,18 @@
+// chatgpt
+function rewriteCookieDomain(rawCookie: string): string {
+ return rawCookie
+ .replace(/;?\s*Domain=[^;]+/i, '')
+ .concat(`; Domain=localhost:3000`);
+}
+
+// chatgpt
async function proxyRequest(request: Request, method: string) {
const { searchParams } = new URL(request.url);
const target = searchParams.get("url");
if (!target) {
return new Response(
- JSON.stringify({ error: "Missing `url` query parameter." }),
+ JSON.stringify({ error: "missing url param, dumbass" }),
{
status: 400,
headers: { "Content-Type": "application/json" }
@@ -16,7 +24,7 @@ async function proxyRequest(request: Request, method: string) {
const headers = new Headers(request.headers);
headers.delete("host");
- headers.delete("accept-encoding"); // ! important
+ headers.delete("accept-encoding");
const init: RequestInit = {
method,
@@ -30,8 +38,20 @@ async function proxyRequest(request: Request, method: string) {
const response = await fetch(targetUrl, init);
+ // Copy all response headers
const responseHeaders = new Headers(response.headers);
- responseHeaders.delete("content-encoding"); // ! important
+ responseHeaders.delete("content-encoding");
+
+ const rawSetCookies = response.headers.getSetCookie?.() ?? [];
+
+ if (rawSetCookies.length > 0) {
+ responseHeaders.delete("set-cookie");
+
+ for (const rawCookie of rawSetCookies) {
+ const rewritten = rewriteCookieDomain(rawCookie);
+ responseHeaders.append("set-cookie", rewritten);
+ }
+ }
return new Response(response.body, {
status: response.status,
diff --git a/bun.lock b/bun.lock
index 8b898b2..e89df99 100644
--- a/bun.lock
+++ b/bun.lock
@@ -6,6 +6,7 @@
"dependencies": {
"@catppuccin/tailwindcss": "^0.1.6",
"@hookform/resolvers": "^5.1.1",
+ "@ocbwoy3/libocbwoy3": "^0.0.5",
"@radix-ui/react-accordion": "^1.2.11",
"@radix-ui/react-alert-dialog": "^1.1.14",
"@radix-ui/react-aspect-ratio": "^1.1.7",
@@ -34,6 +35,7 @@
"@radix-ui/react-toggle-group": "^1.1.10",
"@radix-ui/react-tooltip": "^1.2.7",
"@tailwindcss/line-clamp": "^0.4.4",
+ "@types/bun": "^1.2.19",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
@@ -161,6 +163,8 @@
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
+ "@ocbwoy3/libocbwoy3": ["@ocbwoy3/libocbwoy3@0.0.5", "", {}, "sha512-zDm11Z5xzmOgsDg/E8Z9UFlMYjKdkUg28YE/Fwb0+NoVx5gKyj3D9eSbvBAOfTISTKkI3X2FTWHg3ehvyLz1Fg=="],
+
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
@@ -281,6 +285,8 @@
"@tailwindcss/line-clamp": ["@tailwindcss/line-clamp@0.4.4", "", { "peerDependencies": { "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" } }, "sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g=="],
+ "@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
+
"@types/d3-array": ["@types/d3-array@3.2.1", "", {}, "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="],
"@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
@@ -345,6 +351,8 @@
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+ "bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
+
"busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
"camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
diff --git a/components/site/HomeUserHeader.tsx b/components/site/HomeUserHeader.tsx
index e5b2d3b..aa5392d 100644
--- a/components/site/HomeUserHeader.tsx
+++ b/components/site/HomeUserHeader.tsx
@@ -12,6 +12,18 @@ import { useCurrentAccount } from "@/hooks/roblox/useCurrentAccount";
import { Skeleton } from "../ui/skeleton";
import { useFriendsPresence } from "@/hooks/roblox/usePresence";
import { useAccountSettings } from "@/hooks/roblox/useAccountSettings";
+import { loadThumbnails } from "@/lib/thumbnailLoader";
+import { toast } from "sonner";
+
+// chatgpt + human
+function randomGreeting(name: string): string {
+ const greetings = [
+ `Howdy, ${name}`
+ ];
+
+ const index = Math.floor(Math.random() * greetings.length);
+ return greetings[index];
+}
export function HomeLoggedInHeader() {
const profile = useCurrentAccount();
@@ -52,7 +64,23 @@ export function HomeLoggedInHeader() {
return (
<>
{/* */}
-
+
{
+ if (e.button === 2) {
+ toast("[debug] reloading user pfp");
+ console.log("[debug] reloading user pfp");
+ loadThumbnails([
+ {
+ type: "AvatarHeadShot",
+ targetId: profile ? profile.id : 1,
+ format: "webp",
+ size: "720x720"
+ }
+ ]).catch(() => {});
+ }
+ }}
+ >
{!isLoaded ? (
) : (
@@ -64,14 +92,7 @@ export function HomeLoggedInHeader() {
)}
- {isLoaded ? (
- <>
- {!!accountSettings &&
- accountSettings.IsPremium === true
- ? `Howdy, ${profile.displayName}`
- : `${profile.displayName}`}
- >
- ) : (
+ {isLoaded ? randomGreeting(window.localStorage.UserPreferredName || profile.displayName || "Robloxian!") : (
<>
>
diff --git a/components/site/QuickTopUI.tsx b/components/site/QuickTopUI.tsx
index c1f772b..0009331 100644
--- a/components/site/QuickTopUI.tsx
+++ b/components/site/QuickTopUI.tsx
@@ -3,10 +3,8 @@
import { useRobuxBalance } from "@/hooks/roblox/useRobuxBalance";
import { RobuxIcon } from "../roblox/RobloxIcons";
import React, { useState } from "react";
-import { Separator } from "../ui/separator";
-import { Bell, SettingsIcon, ShirtIcon } from "lucide-react";
+import { ShirtIcon } from "lucide-react";
import { StupidHoverThing } from "../util/MiscStuff";
-import { toast } from "sonner";
import { OutfitSelector } from "./OutfitQuickChooser";
import { proxyFetch } from "@/lib/utils";
import { loadThumbnails } from "@/lib/thumbnailLoader";
@@ -16,14 +14,21 @@ import { useBestFriends } from "@/hooks/roblox/useBestFriends";
import { useCurrentAccount } from "@/hooks/roblox/useCurrentAccount";
import { useFriendsPresence } from "@/hooks/roblox/usePresence";
-/**
-requires csrf token cuz u cant use noblox.js on the web
-
-either go to https://roblox.com/my/avataar or the app to change ur fit
-*/
async function updateOutfit(outfit: { id: number }, acc: { id: number }) {
try {
- const J = (await (
+ // ocbwoy3 stupid idiot for using v3 api
+ const details = (await (
+ await proxyFetch(
+ `https://avatar.roblox.com/v1/outfits/${outfit.id}/details`
+ )
+ ).json()) as {
+ id: number;
+ name: string;
+ bodyColors: Record;
+ scale: Record;
+ };
+
+ const detailsV3 = (await (
await proxyFetch(
`https://avatar.roblox.com/v3/outfits/${outfit.id}/details`
)
@@ -31,16 +36,50 @@ async function updateOutfit(outfit: { id: number }, acc: { id: number }) {
id: number;
name: string;
assets: any[];
+ bodyColors: Record;
+ scale: Record;
+ playerAvatarType: "R6" | "R15";
};
+
+ await proxyFetch(
+ `https://avatar.roblox.com/v1/avatar/set-body-colors`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(details.bodyColors)
+ }
+ );
+
+ await proxyFetch(`https://avatar.roblox.com/v1/avatar/set-scales`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(details.scale)
+ });
+
+ // u cant set avatar item scaling/rotation cuz roblox can't make good web apis
await proxyFetch(
`https://avatar.roblox.com/v1/avatar/set-wearing-assets`,
{
method: "POST",
+ headers: { "Content-Type": "application/json" },
body: JSON.stringify({
- assetIds: J.assets.map((a) => a.id).filter((a) => !!a)
+ assetIds: detailsV3.assets.map((a) => a.id).filter(Boolean)
})
}
);
+
+ await proxyFetch(
+ `https://avatar.roblox.com/v1/avatar/set-player-avatar-type`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ playerAvatarType: detailsV3.playerAvatarType
+ })
+ }
+ );
+
+
loadThumbnails([
{
type: "AvatarHeadShot",
@@ -48,8 +87,10 @@ async function updateOutfit(outfit: { id: number }, acc: { id: number }) {
format: "webp",
size: "720x720"
}
- ]).catch((a) => {});
- } catch {}
+ ]).catch(() => {});
+ } catch (err) {
+ console.error(err);
+ }
}
export const QuickTopUI = React.memo(function () {
@@ -85,12 +126,18 @@ export const QuickTopUI = React.memo(function () {
{robux ? (
-
{robux.toLocaleString()}
+
+ {robux ? robux.toLocaleString() : "???"}
+
) : (
<>>
)}
@@ -109,6 +156,7 @@ export const QuickTopUILogoPart = React.memo(function () {
{"ocbwoy3-chan's roblox"}
+
{process.env.NODE_ENV} {process.env.NEXT_PUBLIC_CWD} {process.env.NEXT_PUBLIC_ARGV0}
);
diff --git a/lib/robloxEngine/BrickColorIds.ts b/lib/robloxEngine/BrickColorIds.ts
new file mode 100644
index 0000000..742d552
--- /dev/null
+++ b/lib/robloxEngine/BrickColorIds.ts
@@ -0,0 +1,283 @@
+export const BrickColors: {
+ name: string;
+ id: number;
+ col: [number, number, number];
+}[] = [
+ { name: "White", id: 1, col: [242, 243, 243] },
+ { name: "Grey", id: 2, col: [161, 165, 162] },
+ { name: "Light yellow", id: 3, col: [249, 233, 153] },
+ { name: "Brick yellow", id: 5, col: [215, 197, 154] },
+ { name: "Light green (Mint)", id: 6, col: [194, 218, 184] },
+ { name: "Light reddish violet", id: 9, col: [232, 186, 200] },
+ { name: "Pastel Blue", id: 11, col: [128, 187, 219] },
+ { name: "Light orange brown", id: 12, col: [203, 132, 66] },
+ { name: "Nougat", id: 18, col: [204, 142, 105] },
+ { name: "Bright red", id: 21, col: [196, 40, 28] },
+ { name: "Med. reddish violet", id: 22, col: [196, 112, 160] },
+ { name: "Bright blue", id: 23, col: [13, 105, 172] },
+ { name: "Bright yellow", id: 24, col: [245, 205, 48] },
+ { name: "Earth orange", id: 25, col: [98, 71, 50] },
+ { name: "Black", id: 26, col: [27, 42, 53] },
+ { name: "Dark grey", id: 27, col: [109, 110, 108] },
+ { name: "Dark green", id: 28, col: [40, 127, 71] },
+ { name: "Medium green", id: 29, col: [161, 196, 140] },
+ { name: "Lig. Yellowich orange", id: 36, col: [243, 207, 155] },
+ { name: "Bright green", id: 37, col: [75, 151, 75] },
+ { name: "Dark orange", id: 38, col: [160, 95, 53] },
+ { name: "Light bluish violet", id: 39, col: [193, 202, 222] },
+ { name: "Transparent", id: 40, col: [236, 236, 236] },
+ { name: "Tr. Red", id: 41, col: [205, 84, 75] },
+ { name: "Tr. Lg blue", id: 42, col: [193, 223, 240] },
+ { name: "Tr. Blue", id: 43, col: [123, 182, 232] },
+ { name: "Tr. Yellow", id: 44, col: [247, 241, 141] },
+ { name: "Light blue", id: 45, col: [180, 210, 228] },
+ { name: "Tr. Flu. Reddish orange", id: 47, col: [217, 133, 108] },
+ { name: "Tr. Green", id: 48, col: [132, 182, 141] },
+ { name: "Tr. Flu. Green", id: 49, col: [248, 241, 132] },
+ { name: "Phosph. White", id: 50, col: [236, 232, 222] },
+ { name: "Light red", id: 100, col: [238, 196, 182] },
+ { name: "Medium red", id: 101, col: [218, 134, 122] },
+ { name: "Medium blue", id: 102, col: [110, 153, 202] },
+ { name: "Light grey", id: 103, col: [199, 193, 183] },
+ { name: "Bright violet", id: 104, col: [107, 50, 124] },
+ { name: "Br. yellowish orange", id: 105, col: [226, 155, 64] },
+ { name: "Bright orange", id: 106, col: [218, 133, 65] },
+ { name: "Bright bluish green", id: 107, col: [0, 143, 156] },
+ { name: "Earth yellow", id: 108, col: [104, 92, 67] },
+ { name: "Bright bluish violet", id: 110, col: [67, 84, 147] },
+ { name: "Tr. Brown", id: 111, col: [191, 183, 177] },
+ { name: "Medium bluish violet", id: 112, col: [104, 116, 172] },
+ { name: "Tr. Medi. reddish violet", id: 113, col: [229, 173, 200] },
+ { name: "Med. yellowish green", id: 115, col: [199, 210, 60] },
+ { name: "Med. bluish green", id: 116, col: [85, 165, 175] },
+ { name: "Light bluish green", id: 118, col: [183, 215, 213] },
+ { name: "Br. yellowish green", id: 119, col: [164, 189, 71] },
+ { name: "Lig. yellowish green", id: 120, col: [217, 228, 167] },
+ { name: "Med. yellowish orange", id: 121, col: [231, 172, 88] },
+ { name: "Br. reddish orange", id: 123, col: [211, 111, 76] },
+ { name: "Bright reddish violet", id: 124, col: [146, 57, 120] },
+ { name: "Light orange", id: 125, col: [234, 184, 146] },
+ { name: "Tr. Bright bluish violet", id: 126, col: [165, 165, 203] },
+ { name: "Gold", id: 127, col: [220, 188, 129] },
+ { name: "Dark nougat", id: 128, col: [174, 122, 89] },
+ { name: "Silver", id: 131, col: [156, 163, 168] },
+ { name: "Neon orange", id: 133, col: [213, 115, 61] },
+ { name: "Neon green", id: 134, col: [216, 221, 86] },
+ { name: "Sand blue", id: 135, col: [116, 134, 157] },
+ { name: "Sand violet", id: 136, col: [135, 124, 144] },
+ { name: "Medium orange", id: 137, col: [224, 152, 100] },
+ { name: "Sand yellow", id: 138, col: [149, 138, 115] },
+ { name: "Earth blue", id: 140, col: [32, 58, 86] },
+ { name: "Earth green", id: 141, col: [39, 70, 45] },
+ { name: "Tr. Flu. Blue", id: 143, col: [207, 226, 247] },
+ { name: "Sand blue metallic", id: 145, col: [121, 136, 161] },
+ { name: "Sand violet metallic", id: 146, col: [149, 142, 163] },
+ { name: "Sand yellow metallic", id: 147, col: [147, 135, 103] },
+ { name: "Dark grey metallic", id: 148, col: [87, 88, 87] },
+ { name: "Black metallic", id: 149, col: [22, 29, 50] },
+ { name: "Light grey metallic", id: 150, col: [171, 173, 172] },
+ { name: "Sand green", id: 151, col: [120, 144, 130] },
+ { name: "Sand red", id: 153, col: [149, 121, 119] },
+ { name: "Dark red", id: 154, col: [123, 46, 47] },
+ { name: "Tr. Flu. Yellow", id: 157, col: [255, 246, 123] },
+ { name: "Tr. Flu. Red", id: 158, col: [225, 164, 194] },
+ { name: "Gun metallic", id: 168, col: [117, 108, 98] },
+ { name: "Red flip/flop", id: 176, col: [151, 105, 91] },
+ { name: "Yellow flip/flop", id: 178, col: [180, 132, 85] },
+ { name: "Silver flip/flop", id: 179, col: [137, 135, 136] },
+ { name: "Curry", id: 180, col: [215, 169, 75] },
+ { name: "Fire Yellow", id: 190, col: [249, 214, 46] },
+ { name: "Flame yellowish orange", id: 191, col: [232, 171, 45] },
+ { name: "Reddish brown", id: 192, col: [105, 64, 40] },
+ { name: "Flame reddish orange", id: 193, col: [207, 96, 36] },
+ { name: "Medium stone grey", id: 194, col: [163, 162, 165] },
+ { name: "Royal blue", id: 195, col: [70, 103, 164] },
+ { name: "Dark Royal blue", id: 196, col: [35, 71, 139] },
+ { name: "Bright reddish lilac", id: 198, col: [142, 66, 133] },
+ { name: "Dark stone grey", id: 199, col: [99, 95, 98] },
+ { name: "Lemon metalic", id: 200, col: [130, 138, 93] },
+ { name: "Light stone grey", id: 208, col: [229, 228, 223] },
+ { name: "Dark Curry", id: 209, col: [176, 142, 68] },
+ { name: "Faded green", id: 210, col: [112, 149, 120] },
+ { name: "Turquoise", id: 211, col: [121, 181, 181] },
+ { name: "Light Royal blue", id: 212, col: [159, 195, 233] },
+ { name: "Medium Royal blue", id: 213, col: [108, 129, 183] },
+ { name: "Rust", id: 216, col: [144, 76, 42] },
+ { name: "Brown", id: 217, col: [124, 92, 70] },
+ { name: "Reddish lilac", id: 218, col: [150, 112, 159] },
+ { name: "Lilac", id: 219, col: [107, 98, 155] },
+ { name: "Light lilac", id: 220, col: [167, 169, 206] },
+ { name: "Bright purple", id: 221, col: [205, 98, 152] },
+ { name: "Light purple", id: 222, col: [228, 173, 200] },
+ { name: "Light pink", id: 223, col: [220, 144, 149] },
+ { name: "Light brick yellow", id: 224, col: [240, 213, 160] },
+ { name: "Warm yellowish orange", id: 225, col: [235, 184, 127] },
+ { name: "Cool yellow", id: 226, col: [253, 234, 141] },
+ { name: "Dove blue", id: 232, col: [125, 187, 221] },
+ { name: "Medium lilac", id: 268, col: [52, 43, 117] },
+ { name: "Slime green", id: 301, col: [80, 109, 84] },
+ { name: "Smoky grey", id: 302, col: [91, 93, 105] },
+ { name: "Dark blue", id: 303, col: [0, 16, 176] },
+ { name: "Parsley green", id: 304, col: [44, 101, 29] },
+ { name: "Steel blue", id: 305, col: [82, 124, 174] },
+ { name: "Storm blue", id: 306, col: [51, 88, 130] },
+ { name: "Lapis", id: 307, col: [16, 42, 220] },
+ { name: "Dark indigo", id: 308, col: [61, 21, 133] },
+ { name: "Sea green", id: 309, col: [52, 142, 64] },
+ { name: "Shamrock", id: 310, col: [91, 154, 76] },
+ { name: "Fossil", id: 311, col: [159, 161, 172] },
+ { name: "Mulberry", id: 312, col: [89, 34, 89] },
+ { name: "Forest green", id: 313, col: [31, 128, 29] },
+ { name: "Cadet blue", id: 314, col: [159, 173, 192] },
+ { name: "Electric blue", id: 315, col: [9, 137, 207] },
+ { name: "Eggplant", id: 316, col: [123, 0, 123] },
+ { name: "Moss", id: 317, col: [124, 156, 107] },
+ { name: "Artichoke", id: 318, col: [138, 171, 133] },
+ { name: "Sage green", id: 319, col: [185, 196, 177] },
+ { name: "Ghost grey", id: 320, col: [202, 203, 209] },
+ { name: "Lilac", id: 321, col: [167, 94, 155] },
+ { name: "Plum", id: 322, col: [123, 47, 123] },
+ { name: "Olivine", id: 323, col: [148, 190, 129] },
+ { name: "Laurel green", id: 324, col: [168, 189, 153] },
+ { name: "Quill grey", id: 325, col: [223, 223, 222] },
+ { name: "Crimson", id: 327, col: [151, 0, 0] },
+ { name: "Mint", id: 328, col: [177, 229, 166] },
+ { name: "Baby blue", id: 329, col: [152, 194, 219] },
+ { name: "Carnation pink", id: 330, col: [255, 152, 220] },
+ { name: "Persimmon", id: 331, col: [255, 89, 89] },
+ { name: "Maroon", id: 332, col: [117, 0, 0] },
+ { name: "Gold", id: 333, col: [239, 184, 56] },
+ { name: "Daisy orange", id: 334, col: [248, 217, 109] },
+ { name: "Pearl", id: 335, col: [231, 231, 236] },
+ { name: "Fog", id: 336, col: [199, 212, 228] },
+ { name: "Salmon", id: 337, col: [255, 148, 148] },
+ { name: "Terra Cotta", id: 338, col: [190, 104, 98] },
+ { name: "Cocoa", id: 339, col: [86, 36, 36] },
+ { name: "Wheat", id: 340, col: [241, 231, 199] },
+ { name: "Buttermilk", id: 341, col: [254, 243, 187] },
+ { name: "Mauve", id: 342, col: [224, 178, 208] },
+ { name: "Sunrise", id: 343, col: [212, 144, 189] },
+ { name: "Tawny", id: 344, col: [150, 85, 85] },
+ { name: "Rust", id: 345, col: [143, 76, 42] },
+ { name: "Cashmere", id: 346, col: [211, 190, 150] },
+ { name: "Khaki", id: 347, col: [226, 220, 188] },
+ { name: "Lily white", id: 348, col: [237, 234, 234] },
+ { name: "Seashell", id: 349, col: [233, 218, 218] },
+ { name: "Burgundy", id: 350, col: [136, 62, 62] },
+ { name: "Cork", id: 351, col: [188, 155, 93] },
+ { name: "Burlap", id: 352, col: [199, 172, 120] },
+ { name: "Beige", id: 353, col: [202, 191, 163] },
+ { name: "Oyster", id: 354, col: [187, 179, 178] },
+ { name: "Pine Cone", id: 355, col: [108, 88, 75] },
+ { name: "Fawn brown", id: 356, col: [160, 132, 79] },
+ { name: "Hurricane grey", id: 357, col: [149, 137, 136] },
+ { name: "Cloudy grey", id: 358, col: [171, 168, 158] },
+ { name: "Linen", id: 359, col: [175, 148, 131] },
+ { name: "Copper", id: 360, col: [150, 103, 102] },
+ { name: "Medium brown", id: 361, col: [86, 66, 54] },
+ { name: "Bronze", id: 362, col: [126, 104, 63] },
+ { name: "Flint", id: 363, col: [105, 102, 92] },
+ { name: "Dark taupe", id: 364, col: [90, 76, 66] },
+ { name: "Burnt Sienna", id: 365, col: [106, 57, 9] },
+ { name: "Institutional white", id: 1001, col: [248, 248, 248] },
+ { name: "Mid gray", id: 1002, col: [205, 205, 205] },
+ { name: "Really black", id: 1003, col: [17, 17, 17] },
+ { name: "Really red", id: 1004, col: [255, 0, 0] },
+ { name: "Deep orange", id: 1005, col: [255, 176, 0] },
+ { name: "Alder", id: 1006, col: [180, 128, 255] },
+ { name: "Dusty Rose", id: 1007, col: [163, 75, 75] },
+ { name: "Olive", id: 1008, col: [193, 190, 66] },
+ { name: "New Yeller", id: 1009, col: [255, 255, 0] },
+ { name: "Really blue", id: 1010, col: [0, 0, 255] },
+ { name: "Navy blue", id: 1011, col: [0, 32, 96] },
+ { name: "Deep blue", id: 1012, col: [33, 84, 185] },
+ { name: "Cyan", id: 1013, col: [4, 175, 236] },
+ { name: "CGA brown", id: 1014, col: [170, 85, 0] },
+ { name: "Magenta", id: 1015, col: [170, 0, 170] },
+ { name: "Pink", id: 1016, col: [255, 102, 204] },
+ { name: "Deep orange", id: 1017, col: [255, 175, 0] },
+ { name: "Teal", id: 1018, col: [18, 238, 212] },
+ { name: "Toothpaste", id: 1019, col: [0, 255, 255] },
+ { name: "Lime green", id: 1020, col: [0, 255, 0] },
+ { name: "Camo", id: 1021, col: [58, 125, 21] },
+ { name: "Grime", id: 1022, col: [127, 142, 100] },
+ { name: "Lavender", id: 1023, col: [140, 91, 159] },
+ { name: "Pastel light blue", id: 1024, col: [175, 221, 255] },
+ { name: "Pastel orange", id: 1025, col: [255, 201, 201] },
+ { name: "Pastel violet", id: 1026, col: [177, 167, 255] },
+ { name: "Pastel blue-green", id: 1027, col: [159, 243, 233] },
+ { name: "Pastel green", id: 1028, col: [204, 255, 204] },
+ { name: "Pastel yellow", id: 1029, col: [255, 255, 204] },
+ { name: "Pastel brown", id: 1030, col: [255, 204, 153] },
+ { name: "Royal purple", id: 1031, col: [98, 37, 209] },
+ { name: "Hot pink", id: 1032, col: [255, 0, 191] }
+];
+
+// chatgpt
+export const BrickColorHexMap: { [hex: string]: number } = Object.fromEntries(
+ BrickColors.map(({ id, col }) => {
+ const hex = rgbToHex(...col);
+ return [hex, id];
+ })
+);
+
+// chatgpt
+function rgbToHex(r: number, g: number, b: number): string {
+ return [r, g, b]
+ .map((v) => {
+ const hex = Math.round(v).toString(16).padStart(2, "0");
+ return hex;
+ })
+ .join("")
+ .toLowerCase();
+}
+
+/**
+ * find the closest roblox brickcolor id of a hex color
+ * made by chatgpt
+ */
+export function findClosestBrickColor(hex: string): {
+ id: number;
+ name: string;
+ col: [number, number, number];
+} {
+ const target = hexToRgb(hex);
+ console.log(hex,target)
+ if (!target) throw new Error("Invalid hex");
+
+ let bestDist = Infinity;
+ let bestColor = BrickColors[0];
+
+ for (const brick of BrickColors) {
+ const [r, g, b] = brick.col;
+ const dist = euclideanDistance(target, [r, g, b]);
+
+ if (dist < bestDist) {
+ bestDist = dist;
+ bestColor = brick;
+ }
+ }
+
+ return bestColor;
+}
+
+// chatgpt
+function hexToRgb(hex?: string): [number, number, number] | null {
+ if (typeof hex !== "string") return null;
+
+ const cleaned = hex.replace(/^#/, "").toLowerCase();
+ if (!/^[0-9a-f]{6}$/.test(cleaned)) return null;
+
+ const r = parseInt(cleaned.slice(0, 2), 16);
+ const g = parseInt(cleaned.slice(2, 4), 16);
+ const b = parseInt(cleaned.slice(4, 6), 16);
+ return [r, g, b];
+}
+
+// chatgpt
+function euclideanDistance(
+ a: [number, number, number],
+ b: [number, number, number]
+) {
+ return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2;
+}
diff --git a/lib/utils.ts b/lib/utils.ts
index e2e45de..17c4501 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -7,10 +7,7 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
-/**
- * ! Do not use in actual code, only used by proxy fetch to fix the CSRF thing
-*/
-async function proxyFetchRaw(
+export async function proxyFetchRaw(
input: RequestInfo | URL,
init?: RequestInit
): Promise {
@@ -18,8 +15,8 @@ async function proxyFetchRaw(
typeof input === "string"
? input
: input instanceof Request
- ? input.url
- : "";
+ ? input.url
+ : "";
const proxyUrl = `/api/proxy?url=${encodeURIComponent(url)}`;
@@ -31,7 +28,7 @@ async function proxyFetchRaw(
...init,
method: init?.method || "GET",
headers,
- body: init?.body
+ body: init?.body,
};
return window.fetch(proxyUrl, fetchInit);
@@ -60,7 +57,7 @@ export async function proxyFetch(
response = await proxyFetchRaw(input, {
...init,
- headers: newHeaders
+ headers: newHeaders,
});
}
diff --git a/next.config.ts b/next.config.ts
index aa0ebd7..71359bd 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,5 +1,14 @@
import type { NextConfig } from "next";
+if (!process.isBun) {
+ console.error(`You are running this with node. Rerun the process: bun --bun run dev`)
+ process.exit(1)
+}
+
+process.env.NEXT_PUBLIC_CWD = __dirname || "~"
+process.env.NEXT_PUBLIC_ARGV0 = process.argv0 || "node"
+
+
const nextConfig: NextConfig = {
/* config options here */
images: {
diff --git a/package.json b/package.json
index 8e9ad63..c2d552e 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"dependencies": {
"@catppuccin/tailwindcss": "^0.1.6",
"@hookform/resolvers": "^5.1.1",
+ "@ocbwoy3/libocbwoy3": "^0.0.5",
"@radix-ui/react-accordion": "^1.2.11",
"@radix-ui/react-alert-dialog": "^1.1.14",
"@radix-ui/react-aspect-ratio": "^1.1.7",
@@ -39,6 +40,7 @@
"@radix-ui/react-toggle-group": "^1.1.10",
"@radix-ui/react-tooltip": "^1.2.7",
"@tailwindcss/line-clamp": "^0.4.4",
+ "@types/bun": "^1.2.19",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",