Rewrite with modern stack
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:
parent
5eb7a1482e
commit
98f290a662
69 changed files with 17771 additions and 1589 deletions
80
frontend/src/components/ui/CooldownTimer.tsx
Normal file
80
frontend/src/components/ui/CooldownTimer.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface CooldownTimerProps {
|
||||
isActive: boolean;
|
||||
duration: number; // in seconds
|
||||
onComplete: () => void;
|
||||
}
|
||||
|
||||
export function CooldownTimer({ isActive, duration, onComplete }: CooldownTimerProps) {
|
||||
const [timeLeft, setTimeLeft] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (isActive) {
|
||||
setTimeLeft(duration);
|
||||
const interval = setInterval(() => {
|
||||
setTimeLeft((prev) => {
|
||||
if (prev <= 1) {
|
||||
clearInterval(interval);
|
||||
// Use setTimeout to avoid setState during render
|
||||
setTimeout(() => onComplete(), 0);
|
||||
return 0;
|
||||
}
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [isActive, duration, onComplete]);
|
||||
|
||||
if (!isActive || timeLeft === 0) return null;
|
||||
|
||||
const progress = ((duration - timeLeft) / duration) * 100;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="fixed top-4 sm:top-6 left-1/2 transform -translate-x-1/2 z-50"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.8 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
stiffness: 400,
|
||||
damping: 25
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
className="bg-white/5 backdrop-blur-2xl rounded-full px-4 sm:px-6 py-2 sm:py-3 border border-white/20 shadow-lg ring-1 ring-white/10"
|
||||
whileHover={{
|
||||
scale: 1.05
|
||||
}}
|
||||
transition={{ type: "spring", stiffness: 500, damping: 25 }}
|
||||
>
|
||||
<div className="flex items-center gap-3 sm:gap-4">
|
||||
<motion.div
|
||||
className="text-white font-medium text-sm"
|
||||
animate={{
|
||||
color: timeLeft <= 3 ? "#f87171" : "#ffffff"
|
||||
}}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
{timeLeft}s
|
||||
</motion.div>
|
||||
|
||||
<div className="w-16 sm:w-24 h-2 bg-white/20 rounded-full overflow-hidden">
|
||||
<motion.div
|
||||
className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full"
|
||||
initial={{ width: 0 }}
|
||||
animate={{ width: `${progress}%` }}
|
||||
transition={{ duration: 0.5, ease: "easeOut" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue