From 850630fbc66e5e8001afd0d460317c3fe1f950ec Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:42:55 +0000 Subject: [PATCH] Changes --- src/components/HumanVerification.tsx | 397 ++++++++++++++++++-------- src/components/Sidebar.tsx | 6 +- src/components/SliderVerification.tsx | 318 --------------------- 3 files changed, 273 insertions(+), 448 deletions(-) delete mode 100644 src/components/SliderVerification.tsx diff --git a/src/components/HumanVerification.tsx b/src/components/HumanVerification.tsx index 83584f4..8a2c441 100644 --- a/src/components/HumanVerification.tsx +++ b/src/components/HumanVerification.tsx @@ -9,23 +9,22 @@ interface HumanVerificationProps { onVerified: () => void; } -interface PuzzlePiece { - targetX: number; -} - const HumanVerification = ({ onVerified }: HumanVerificationProps) => { const canvasRef = useRef(null); - const [puzzle, setPuzzle] = useState(null); const [isDragging, setIsDragging] = useState(false); const [sliderX, setSliderX] = useState(0); const [isVerified, setIsVerified] = useState(false); const [error, setError] = useState(false); + const [puzzleImage, setPuzzleImage] = useState(null); + const [targetX, setTargetX] = useState(0); const trackRef = useRef(null); - const CANVAS_WIDTH = 320; - const CANVAS_HEIGHT = 160; - const PIECE_SIZE = 44; - const TOLERANCE = 10; + const CANVAS_WIDTH = 300; + const CANVAS_HEIGHT = 150; + const PIECE_WIDTH = 42; + const PIECE_HEIGHT = 42; + const NOTCH_SIZE = 10; + const TOLERANCE = 6; // Check for bypass in URL on mount useEffect(() => { @@ -36,9 +35,156 @@ const HumanVerification = ({ onVerified }: HumanVerificationProps) => { } }, [onVerified]); + const drawPuzzlePiecePath = (ctx: CanvasRenderingContext2D, x: number, y: number) => { + const w = PIECE_WIDTH; + const h = PIECE_HEIGHT; + const n = NOTCH_SIZE; + + ctx.beginPath(); + ctx.moveTo(x, y); + // Top edge + ctx.lineTo(x + w, y); + // Right edge with notch + ctx.lineTo(x + w, y + h * 0.35); + ctx.arc(x + w + n * 0.5, y + h * 0.5, n, -Math.PI * 0.5, Math.PI * 0.5, false); + ctx.lineTo(x + w, y + h); + // Bottom edge + ctx.lineTo(x, y + h); + // Left edge + ctx.lineTo(x, y); + ctx.closePath(); + }; + const generatePuzzle = useCallback(() => { - const targetX = Math.floor(Math.random() * (CANVAS_WIDTH - PIECE_SIZE - 100)) + 80; - setPuzzle({ targetX }); + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + // Get theme color + const computedStyle = getComputedStyle(document.documentElement); + const primaryHsl = computedStyle.getPropertyValue('--primary').trim(); + const hslParts = primaryHsl.split(' '); + const h = parseFloat(hslParts[0]) || 120; + const s = parseFloat(hslParts[1]) || 100; + const l = parseFloat(hslParts[2]) || 50; + + // Clear canvas + ctx.fillStyle = '#0d0d0d'; + ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + + // Create a visually interesting background pattern + // Gradient base + const gradient = ctx.createLinearGradient(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + gradient.addColorStop(0, `hsla(${h}, ${s}%, ${l * 0.15}%, 1)`); + gradient.addColorStop(0.5, `hsla(${h}, ${s}%, ${l * 0.08}%, 1)`); + gradient.addColorStop(1, `hsla(${h}, ${s}%, ${l * 0.12}%, 1)`); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + + // Draw circuit-like patterns + ctx.strokeStyle = `hsla(${h}, ${s}%, ${l}%, 0.15)`; + ctx.lineWidth = 1; + for (let i = 0; i < 12; i++) { + const startX = Math.random() * CANVAS_WIDTH; + const startY = Math.random() * CANVAS_HEIGHT; + ctx.beginPath(); + ctx.moveTo(startX, startY); + // Draw horizontal then vertical line + const midX = startX + (Math.random() - 0.5) * 80; + ctx.lineTo(midX, startY); + ctx.lineTo(midX, startY + (Math.random() - 0.5) * 60); + ctx.stroke(); + // Draw node dots + ctx.fillStyle = `hsla(${h}, ${s}%, ${l}%, 0.3)`; + ctx.beginPath(); + ctx.arc(startX, startY, 2, 0, Math.PI * 2); + ctx.fill(); + } + + // Matrix-style falling characters + ctx.font = '11px monospace'; + ctx.fillStyle = `hsla(${h}, ${s}%, ${l}%, 0.2)`; + const chars = '01アイウエオカキクケコ田由甲申電网'; + for (let i = 0; i < 40; i++) { + const cx = Math.random() * CANVAS_WIDTH; + const cy = Math.random() * CANVAS_HEIGHT; + ctx.fillText(chars[Math.floor(Math.random() * chars.length)], cx, cy); + } + + // Generate target position (where the piece should go) + const newTargetX = Math.floor(Math.random() * (CANVAS_WIDTH - PIECE_WIDTH - 100)) + 80; + const targetY = (CANVAS_HEIGHT - PIECE_HEIGHT) / 2; + setTargetX(newTargetX); + + // Draw the target slot (dark cutout with glow) + ctx.save(); + drawPuzzlePiecePath(ctx, newTargetX, targetY); + ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; + ctx.fill(); + ctx.strokeStyle = `hsla(${h}, ${s}%, ${l}%, 0.5)`; + ctx.lineWidth = 1.5; + ctx.stroke(); + + // Inner shadow effect + ctx.shadowColor = `hsla(${h}, ${s}%, ${l}%, 0.3)`; + ctx.shadowBlur = 4; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.stroke(); + ctx.restore(); + + // Extract the puzzle piece image from a slightly different position for variation + const extractX = 10; + const extractY = targetY; + + // Create puzzle piece appearance + const pieceCanvas = document.createElement('canvas'); + pieceCanvas.width = PIECE_WIDTH + NOTCH_SIZE + 4; + pieceCanvas.height = PIECE_HEIGHT + 4; + const pieceCtx = pieceCanvas.getContext('2d'); + if (pieceCtx) { + // Draw piece background with pattern + pieceCtx.fillStyle = `hsla(${h}, ${s}%, ${l}%, 0.9)`; + drawPuzzlePiecePath(pieceCtx, 2, 2); + pieceCtx.fill(); + + // Add texture + pieceCtx.strokeStyle = `hsla(${h}, ${s}%, ${l * 1.3}%, 0.6)`; + pieceCtx.lineWidth = 1.5; + drawPuzzlePiecePath(pieceCtx, 2, 2); + pieceCtx.stroke(); + + // Inner pattern + pieceCtx.strokeStyle = 'rgba(0, 0, 0, 0.3)'; + pieceCtx.lineWidth = 1; + pieceCtx.strokeRect(8, 8, PIECE_WIDTH - 12, PIECE_HEIGHT - 12); + + // Arrow indicator + pieceCtx.fillStyle = 'rgba(0, 0, 0, 0.5)'; + pieceCtx.beginPath(); + const arrowCx = PIECE_WIDTH / 2 + 2; + const arrowCy = PIECE_HEIGHT / 2 + 2; + pieceCtx.moveTo(arrowCx - 6, arrowCy - 5); + pieceCtx.lineTo(arrowCx + 6, arrowCy); + pieceCtx.lineTo(arrowCx - 6, arrowCy + 5); + pieceCtx.closePath(); + pieceCtx.fill(); + + setPuzzleImage(pieceCtx.getImageData(0, 0, pieceCanvas.width, pieceCanvas.height)); + } + + // Draw scanlines + ctx.fillStyle = 'rgba(0, 0, 0, 0.06)'; + for (let y = 0; y < CANVAS_HEIGHT; y += 2) { + ctx.fillRect(0, y, CANVAS_WIDTH, 1); + } + + // Border + ctx.strokeStyle = `hsla(${h}, ${s}%, ${l}%, 0.4)`; + ctx.lineWidth = 2; + ctx.strokeRect(1, 1, CANVAS_WIDTH - 2, CANVAS_HEIGHT - 2); + setSliderX(0); setError(false); setIsVerified(false); @@ -48,9 +194,20 @@ const HumanVerification = ({ onVerified }: HumanVerificationProps) => { generatePuzzle(); }, [generatePuzzle]); + // Draw the puzzle piece at current position useEffect(() => { - if (!puzzle || !canvasRef.current) return; + if (!canvasRef.current || !puzzleImage) return; + const canvas = canvasRef.current; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + // Regenerate base image + generatePuzzle(); + }, []); + + // Redraw canvas with piece at current slider position + const redrawWithPiece = useCallback(() => { + if (!canvasRef.current || !puzzleImage) return; const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); if (!ctx) return; @@ -62,121 +219,118 @@ const HumanVerification = ({ onVerified }: HumanVerificationProps) => { const h = parseFloat(hslParts[0]) || 120; const s = parseFloat(hslParts[1]) || 100; const l = parseFloat(hslParts[2]) || 50; - const primaryColor = `hsl(${h}, ${s}%, ${l}%)`; - const dimColor = `hsla(${h}, ${s}%, ${l}%, 0.3)`; - const bgColor = `hsla(${h}, ${s}%, ${l}%, 0.05)`; - // Clear canvas with dark background - ctx.fillStyle = '#0a0a0a'; + // Clear and redraw background + ctx.fillStyle = '#0d0d0d'; ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); - // Draw matrix grid - ctx.strokeStyle = dimColor; - ctx.lineWidth = 0.5; - for (let x = 0; x < CANVAS_WIDTH; x += 16) { + const gradient = ctx.createLinearGradient(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + gradient.addColorStop(0, `hsla(${h}, ${s}%, ${l * 0.15}%, 1)`); + gradient.addColorStop(0.5, `hsla(${h}, ${s}%, ${l * 0.08}%, 1)`); + gradient.addColorStop(1, `hsla(${h}, ${s}%, ${l * 0.12}%, 1)`); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + + // Circuit patterns + ctx.strokeStyle = `hsla(${h}, ${s}%, ${l}%, 0.15)`; + ctx.lineWidth = 1; + for (let i = 0; i < 12; i++) { + const startX = (i * 37) % CANVAS_WIDTH; + const startY = (i * 23) % CANVAS_HEIGHT; ctx.beginPath(); - ctx.moveTo(x, 0); - ctx.lineTo(x, CANVAS_HEIGHT); - ctx.stroke(); - } - for (let y = 0; y < CANVAS_HEIGHT; y += 16) { - ctx.beginPath(); - ctx.moveTo(0, y); - ctx.lineTo(CANVAS_WIDTH, y); + ctx.moveTo(startX, startY); + ctx.lineTo(startX + 40, startY); + ctx.lineTo(startX + 40, startY + 30); ctx.stroke(); } - // Draw random matrix code in background - ctx.font = '10px monospace'; - ctx.fillStyle = `hsla(${h}, ${s}%, ${l}%, 0.15)`; + // Matrix chars + ctx.font = '11px monospace'; + ctx.fillStyle = `hsla(${h}, ${s}%, ${l}%, 0.2)`; + const chars = '01アイウエオ田由甲申'; for (let i = 0; i < 30; i++) { - const x = Math.random() * CANVAS_WIDTH; - const y = Math.random() * CANVAS_HEIGHT; - const chars = '01アイウエオカキクケコサシスセソ'; - ctx.fillText(chars[Math.floor(Math.random() * chars.length)], x, y); + ctx.fillText(chars[i % chars.length], (i * 17) % CANVAS_WIDTH, (i * 13) % CANVAS_HEIGHT); } - // Target slot position - const slotY = (CANVAS_HEIGHT - PIECE_SIZE) / 2; - - // Draw target slot with glow - ctx.shadowColor = primaryColor; - ctx.shadowBlur = 8; - ctx.fillStyle = bgColor; - ctx.strokeStyle = dimColor; - ctx.lineWidth = 2; - - // Draw slot shape with puzzle notch - ctx.beginPath(); - ctx.roundRect(puzzle.targetX, slotY, PIECE_SIZE, PIECE_SIZE, 4); + const pieceY = (CANVAS_HEIGHT - PIECE_HEIGHT) / 2; + + // Draw target slot + ctx.save(); + drawPuzzlePiecePath(ctx, targetX, pieceY); + ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.fill(); + ctx.strokeStyle = `hsla(${h}, ${s}%, ${l}%, 0.5)`; + ctx.lineWidth = 1.5; ctx.stroke(); + ctx.restore(); + + // Calculate piece position from slider + const maxSlide = trackRef.current ? trackRef.current.clientWidth - 48 : 240; + const pieceX = (sliderX / maxSlide) * (CANVAS_WIDTH - PIECE_WIDTH - NOTCH_SIZE - 10) + 5; + + // Draw draggable piece with shadow/glow + ctx.save(); + + let pieceColor = `hsla(${h}, ${s}%, ${l}%, 0.95)`; + let borderColor = `hsla(${h}, ${s}%, ${l * 1.2}%, 1)`; + let glowColor = `hsla(${h}, ${s}%, ${l}%, 0.6)`; - // Inner slot pattern - ctx.shadowBlur = 0; - ctx.strokeStyle = `hsla(${h}, ${s}%, ${l}%, 0.2)`; - ctx.lineWidth = 1; - ctx.strokeRect(puzzle.targetX + 8, slotY + 8, PIECE_SIZE - 16, PIECE_SIZE - 16); - ctx.strokeRect(puzzle.targetX + 14, slotY + 14, PIECE_SIZE - 28, PIECE_SIZE - 28); - - // Calculate piece position based on slider - const maxSlide = trackRef.current ? trackRef.current.clientWidth - 48 : 280; - const pieceX = (sliderX / maxSlide) * (CANVAS_WIDTH - PIECE_SIZE - 10) + 5; - const pieceY = slotY; - - // Determine piece color - let pieceColor = primaryColor; - let pieceBorderColor = primaryColor; if (error) { - pieceColor = 'hsl(0, 70%, 50%)'; - pieceBorderColor = 'hsl(0, 70%, 60%)'; + pieceColor = 'hsla(0, 70%, 50%, 0.95)'; + borderColor = 'hsl(0, 70%, 60%)'; + glowColor = 'hsla(0, 70%, 50%, 0.6)'; } else if (isVerified) { - pieceColor = 'hsl(142, 76%, 36%)'; - pieceBorderColor = 'hsl(142, 76%, 50%)'; + pieceColor = 'hsla(142, 76%, 40%, 0.95)'; + borderColor = 'hsl(142, 76%, 55%)'; + glowColor = 'hsla(142, 76%, 40%, 0.6)'; } - // Draw puzzle piece with glow - ctx.shadowColor = pieceBorderColor; + // Glow effect + ctx.shadowColor = glowColor; ctx.shadowBlur = 12; + + drawPuzzlePiecePath(ctx, pieceX, pieceY); ctx.fillStyle = pieceColor; - ctx.strokeStyle = pieceBorderColor; - ctx.lineWidth = 2; - - ctx.beginPath(); - ctx.roundRect(pieceX, pieceY, PIECE_SIZE, PIECE_SIZE, 4); ctx.fill(); + ctx.strokeStyle = borderColor; + ctx.lineWidth = 2; ctx.stroke(); - + ctx.shadowBlur = 0; - // Draw inner pattern on piece - ctx.strokeStyle = 'rgba(0, 0, 0, 0.4)'; + // Inner pattern on piece + ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)'; ctx.lineWidth = 1; - ctx.strokeRect(pieceX + 8, pieceY + 8, PIECE_SIZE - 16, PIECE_SIZE - 16); - - // Draw arrow on piece - ctx.fillStyle = 'rgba(0, 0, 0, 0.6)'; + ctx.strokeRect(pieceX + 6, pieceY + 6, PIECE_WIDTH - 12, PIECE_HEIGHT - 12); + + // Arrow + ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; ctx.beginPath(); - const arrowX = pieceX + PIECE_SIZE / 2; - const arrowY = pieceY + PIECE_SIZE / 2; - ctx.moveTo(arrowX - 6, arrowY - 5); - ctx.lineTo(arrowX + 6, arrowY); - ctx.lineTo(arrowX - 6, arrowY + 5); + const arrowX = pieceX + PIECE_WIDTH / 2; + const arrowY = pieceY + PIECE_HEIGHT / 2; + ctx.moveTo(arrowX - 5, arrowY - 4); + ctx.lineTo(arrowX + 5, arrowY); + ctx.lineTo(arrowX - 5, arrowY + 4); ctx.closePath(); ctx.fill(); + ctx.restore(); - // Draw scanlines - ctx.fillStyle = 'rgba(0, 0, 0, 0.08)'; + // Scanlines + ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; for (let y = 0; y < CANVAS_HEIGHT; y += 2) { ctx.fillRect(0, y, CANVAS_WIDTH, 1); } - // Draw border - ctx.strokeStyle = dimColor; - ctx.lineWidth = 1; - ctx.strokeRect(0.5, 0.5, CANVAS_WIDTH - 1, CANVAS_HEIGHT - 1); + // Border + ctx.strokeStyle = `hsla(${h}, ${s}%, ${l}%, 0.4)`; + ctx.lineWidth = 2; + ctx.strokeRect(1, 1, CANVAS_WIDTH - 2, CANVAS_HEIGHT - 2); - }, [puzzle, sliderX, error, isVerified]); + }, [sliderX, error, isVerified, targetX, puzzleImage]); + + useEffect(() => { + redrawWithPiece(); + }, [sliderX, error, isVerified, redrawWithPiece]); const handleMouseDown = () => { setIsDragging(true); @@ -191,23 +345,23 @@ const HumanVerification = ({ onVerified }: HumanVerificationProps) => { }; const handleMouseUp = () => { - if (!isDragging || !puzzle || !trackRef.current) return; + if (!isDragging || !trackRef.current) return; setIsDragging(false); const maxSlide = trackRef.current.clientWidth - 48; - const pieceX = (sliderX / maxSlide) * (CANVAS_WIDTH - PIECE_SIZE - 10) + 5; - const diff = Math.abs(pieceX - puzzle.targetX); + const pieceX = (sliderX / maxSlide) * (CANVAS_WIDTH - PIECE_WIDTH - NOTCH_SIZE - 10) + 5; + const diff = Math.abs(pieceX - targetX); if (diff <= TOLERANCE) { setIsVerified(true); localStorage.setItem(VERIFIED_KEY, 'true'); - setTimeout(() => onVerified(), 600); + setTimeout(() => onVerified(), 500); } else { setError(true); setTimeout(() => { setSliderX(0); setError(false); - }, 400); + }, 350); } }; @@ -229,40 +383,35 @@ const HumanVerification = ({ onVerified }: HumanVerificationProps) => { {/* Header */} -
+
- SECURITY VERIFICATION v3.0 + SECURITY VERIFICATION
-
- ANTI-BOT PROTOCOL +
+ ANTI-BOT PROTOCOL v3.0
- {/* Instructions */} -
- {'>'} Slide the piece to complete the puzzle -
- {/* Canvas puzzle area */} -
+
{/* Slider track */}
{ > {/* Track label */}
- - {isVerified ? '[ ACCESS GRANTED ]' : '>>> SLIDE TO VERIFY >>>'} + {isVerified ? '[ VERIFIED ]' : 'DRAG TO COMPLETE PUZZLE'}
@@ -290,31 +439,25 @@ const HumanVerification = ({ onVerified }: HumanVerificationProps) => { onMouseDown={handleMouseDown} onTouchStart={handleTouchStart} animate={error ? { x: [0, -4, 4, -4, 4, 0] } : {}} - transition={{ duration: 0.25 }} + transition={{ duration: 0.2 }} >
- {/* Refresh */} -
- Protocol: SLIDER-VERIFY-3.0 + {/* Controls */} +
- - {/* Footer */} -
- // This verification helps protect against automated access -
); }; -export default HumanVerification; +export default HumanVerification; \ No newline at end of file diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index deb8c90..611b54e 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -77,7 +77,7 @@ const Sidebar = () => { {/* Desktop Navigation - Always visible */} -