Add basic structure for a collaborative pixel art website

Adds the core project structure, including configuration files, a basic HTML page, and the main application component. It also lays the groundwork for the canvas, color palette, and configuration modal functionalities.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 0385ea33-cde8-4bbd-8fce-8d192d30eb41
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/870d08ce-da3b-4822-9874-c2fe2b7628b1/0385ea33-cde8-4bbd-8fce-8d192d30eb41/Vuy7IOw
This commit is contained in:
freesemar93
2025-08-18 12:13:30 +00:00
parent e37c40e945
commit de5e7bfc6c
78 changed files with 16126 additions and 0 deletions

33
client/src/lib/config.ts Normal file
View File

@@ -0,0 +1,33 @@
export const COLORS = [
"#FF0000", // Red
"#FFA500", // Orange
"#FFFF00", // Yellow
"#00FF00", // Green
"#0000FF", // Blue
"#800080", // Purple
"#FFC0CB", // Pink
"#A52A2A", // Brown
"#000000", // Black
"#404040", // Dark Gray
"#808080", // Gray
"#C0C0C0", // Light Gray
"#FFFFFF", // White
"#00FFFF", // Cyan
"#00FF80", // Lime
"#4B0082", // Indigo
] as const;
export const DEFAULT_SELECTED_COLOR = "#FF0000";
export function generateUserId(): string {
const stored = localStorage.getItem("r-place-user-id");
if (stored) return stored;
const newId = `User#${Math.floor(Math.random() * 10000)}`;
localStorage.setItem("r-place-user-id", newId);
return newId;
}
export function getUsername(): string {
return generateUserId();
}

View File

@@ -0,0 +1,57 @@
import { QueryClient, QueryFunction } from "@tanstack/react-query";
async function throwIfResNotOk(res: Response) {
if (!res.ok) {
const text = (await res.text()) || res.statusText;
throw new Error(`${res.status}: ${text}`);
}
}
export async function apiRequest(
method: string,
url: string,
data?: unknown | undefined,
): Promise<Response> {
const res = await fetch(url, {
method,
headers: data ? { "Content-Type": "application/json" } : {},
body: data ? JSON.stringify(data) : undefined,
credentials: "include",
});
await throwIfResNotOk(res);
return res;
}
type UnauthorizedBehavior = "returnNull" | "throw";
export const getQueryFn: <T>(options: {
on401: UnauthorizedBehavior;
}) => QueryFunction<T> =
({ on401: unauthorizedBehavior }) =>
async ({ queryKey }) => {
const res = await fetch(queryKey.join("/") as string, {
credentials: "include",
});
if (unauthorizedBehavior === "returnNull" && res.status === 401) {
return null;
}
await throwIfResNotOk(res);
return await res.json();
};
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryFn: getQueryFn({ on401: "throw" }),
refetchInterval: false,
refetchOnWindowFocus: false,
staleTime: Infinity,
retry: false,
},
mutations: {
retry: false,
},
},
});

6
client/src/lib/utils.ts Normal file
View File

@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}