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>
69 lines
No EOL
2.1 KiB
TypeScript
69 lines
No EOL
2.1 KiB
TypeScript
'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>
|
|
);
|
|
} |