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,69 @@
'use client';
import { motion } from 'framer-motion';
interface StatsOverlayProps {
onlineUsers: number;
totalPixels: number;
zoom: number;
}
export function StatsOverlay({ onlineUsers, totalPixels, zoom }: StatsOverlayProps) {
return (
<motion.div
className="fixed top-4 left-4 sm:top-6 sm:left-6 z-50"
initial={{ opacity: 0, x: -30 }}
animate={{ opacity: 1, x: 0 }}
transition={{
type: "spring",
stiffness: 400,
damping: 25
}}
>
<motion.div
className="bg-white/5 backdrop-blur-2xl rounded-xl sm:rounded-2xl p-3 sm:p-4 border border-white/20 shadow-lg ring-1 ring-white/10"
whileHover={{
scale: 1.05,
boxShadow: "0 10px 25px rgba(0,0,0,0.3)"
}}
transition={{ type: "spring", stiffness: 400, damping: 25 }}
>
<div className="space-y-2 sm:space-y-3">
<motion.div
className="flex items-center gap-2 sm:gap-3"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.1 }}
>
<motion.div
className="w-2.5 h-2.5 sm:w-3 sm:h-3 bg-green-400 rounded-full"
animate={{
scale: [1, 1.3, 1]
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut"
}}
/>
<span className="text-white font-medium text-xs sm:text-sm">
{onlineUsers} online
</span>
</motion.div>
<motion.div
className="flex items-center gap-2 sm:gap-3"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.2 }}
>
<div className="w-2.5 h-2.5 sm:w-3 sm:h-3 bg-blue-400 rounded-full" />
<span className="text-white font-medium text-xs sm:text-sm">
{totalPixels.toLocaleString()} pixels
</span>
</motion.div>
</div>
</motion.div>
</motion.div>
);
}