Modernize collaborative pixel art platform to production-ready architecture

Major refactor from simple HTML/JS app to modern full-stack TypeScript application:

## Architecture Changes
- Migrated to monorepo structure with workspaces (backend, frontend, shared)
- Backend: Node.js + Express + TypeScript + Socket.IO
- Frontend: Next.js 15.5 + React 19 + TypeScript + Tailwind CSS
- Shared: Common types and utilities across packages

## Key Features Implemented
- Real-time WebSocket collaboration via Socket.IO
- Virtual canvas with chunked loading for performance
- Modern UI with dark mode and responsive design
- Mock database system for easy development (Redis/PostgreSQL compatible)
- Comprehensive error handling and rate limiting
- User presence and cursor tracking
- Infinite canvas support with zoom/pan controls

## Performance Optimizations
- Canvas virtualization - only renders visible viewport
- Chunked pixel data loading (64x64 pixel chunks)
- Optimized WebSocket protocol
- Memory-efficient state management with Zustand

## Development Experience
- Full TypeScript support across all packages
- Hot reload for both frontend and backend
- Docker support for production deployment
- Comprehensive linting and formatting
- Automated development server startup

## Fixed Issues
- Corrected start script paths
- Updated environment configuration
- Fixed ESLint configuration issues
- Ensured all dependencies are properly installed
- Verified build process works correctly

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
martin 2025-08-22 19:28:05 +02:00
commit 1da96f34a6
69 changed files with 17771 additions and 1589 deletions

View file

@ -0,0 +1,76 @@
'use client';
import { motion } from 'framer-motion';
interface CoordinateDisplayProps {
x: number;
y: number;
pixelColor?: string | null;
pixelOwner?: string | null;
zoom: number;
}
export function CoordinateDisplay({
x,
y,
pixelColor,
pixelOwner,
zoom
}: CoordinateDisplayProps) {
return (
<motion.div
className="fixed bottom-4 left-4 sm:bottom-6 sm:left-6 z-50"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 30 }}
transition={{
type: "spring",
stiffness: 400,
damping: 25
}}
>
<motion.div
className="bg-white/5 backdrop-blur-2xl rounded-xl sm:rounded-2xl px-3 sm:px-4 py-2 sm:py-3 border border-white/20 shadow-lg ring-1 ring-white/10"
whileHover={{
scale: 1.02
}}
transition={{ type: "spring", stiffness: 400, damping: 25 }}
>
<div className="space-y-1 sm:space-y-2 text-xs sm:text-sm">
{/* Coordinates */}
<div className="flex items-center gap-2">
<span className="text-white/70 font-medium">Pos:</span>
<span className="text-white font-mono font-bold">
({x}, {y})
</span>
</div>
{/* Pixel info */}
{pixelColor && (
<>
<div className="flex items-center gap-2">
<span className="text-white/70 font-medium">Pixel:</span>
<div
className="w-3 h-3 sm:w-4 sm:h-4 rounded border border-white/30"
style={{ backgroundColor: pixelColor }}
/>
<span className="text-white font-mono text-xs">
{pixelColor.toUpperCase()}
</span>
</div>
{pixelOwner && (
<div className="flex items-center gap-2">
<span className="text-white/70 font-medium">By:</span>
<span className="text-blue-300 font-medium text-xs max-w-[80px] sm:max-w-none truncate">
{pixelOwner}
</span>
</div>
)}
</>
)}
</div>
</motion.div>
</motion.div>
);
}