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
51 lines
No EOL
2.1 KiB
TypeScript
51 lines
No EOL
2.1 KiB
TypeScript
'use client';
|
||
|
||
import { motion } from 'framer-motion';
|
||
|
||
interface ZoomControlsProps {
|
||
zoom: number;
|
||
onZoomIn: () => void;
|
||
onZoomOut: () => void;
|
||
}
|
||
|
||
export function ZoomControls({ zoom, onZoomIn, onZoomOut }: ZoomControlsProps) {
|
||
return (
|
||
<motion.div
|
||
className="fixed bottom-4 right-4 sm:bottom-6 sm:right-6 z-40"
|
||
initial={{ opacity: 0, scale: 0.8 }}
|
||
animate={{ opacity: 1, scale: 1 }}
|
||
transition={{ type: "spring", stiffness: 400, damping: 25 }}
|
||
>
|
||
<div className="flex flex-col gap-2 sm:gap-3">
|
||
{/* Zoom In Button */}
|
||
<motion.button
|
||
className="w-12 h-12 sm:w-14 sm:h-14 bg-white/10 backdrop-blur-2xl rounded-full border border-white/20 shadow-lg ring-1 ring-white/10 flex items-center justify-center text-white text-xl sm:text-2xl font-bold hover:bg-white/20 active:bg-white/30 transition-all duration-200 select-none touch-manipulation"
|
||
onClick={onZoomIn}
|
||
whileHover={{ scale: 1.1 }}
|
||
whileTap={{ scale: 0.9 }}
|
||
transition={{ type: "spring", stiffness: 400 }}
|
||
>
|
||
+
|
||
</motion.button>
|
||
|
||
{/* Zoom Out Button */}
|
||
<motion.button
|
||
className="w-12 h-12 sm:w-14 sm:h-14 bg-white/10 backdrop-blur-2xl rounded-full border border-white/20 shadow-lg ring-1 ring-white/10 flex items-center justify-center text-white text-xl sm:text-2xl font-bold hover:bg-white/20 active:bg-white/30 transition-all duration-200 select-none touch-manipulation"
|
||
onClick={onZoomOut}
|
||
whileHover={{ scale: 1.1 }}
|
||
whileTap={{ scale: 0.9 }}
|
||
transition={{ type: "spring", stiffness: 400 }}
|
||
>
|
||
−
|
||
</motion.button>
|
||
|
||
{/* Zoom Display */}
|
||
<div className="bg-white/5 backdrop-blur-2xl rounded-xl sm:rounded-2xl px-2 py-1 sm:px-3 sm:py-2 border border-white/20 shadow-lg ring-1 ring-white/10">
|
||
<div className="text-white font-bold text-xs sm:text-sm text-center min-w-[40px] sm:min-w-[50px]">
|
||
{Math.round(zoom * 100)}%
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
);
|
||
} |