From 4c09953d9b82e737039bb08c75ae5b5e98663e19 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Fri, 16 Jan 2026 18:15:42 +0000
Subject: [PATCH] Changes
---
src/hooks/useSwipeControls.ts | 139 ++++++++++++++++++----------------
src/pages/Games.tsx | 20 ++---
2 files changed, 80 insertions(+), 79 deletions(-)
diff --git a/src/hooks/useSwipeControls.ts b/src/hooks/useSwipeControls.ts
index c76c845..824afc4 100644
--- a/src/hooks/useSwipeControls.ts
+++ b/src/hooks/useSwipeControls.ts
@@ -19,6 +19,7 @@ interface UseSwipeControlsOptions {
/**
* Hook for detecting swipe gestures on touch devices
* Supports directional swipes and fast swipe detection (for hard drop in Tetris)
+ * Attaches to document for full-screen touch detection
*/
export const useSwipeControls = ({
onSwipe,
@@ -28,78 +29,82 @@ export const useSwipeControls = ({
enabled = true,
}: UseSwipeControlsOptions) => {
const swipeStateRef = useRef(null);
+ const onSwipeRef = useRef(onSwipe);
+ const onTapRef = useRef(onTap);
- const handleTouchStart = useCallback((e: TouchEvent) => {
+ // Keep refs updated
+ useEffect(() => {
+ onSwipeRef.current = onSwipe;
+ onTapRef.current = onTap;
+ }, [onSwipe, onTap]);
+
+ useEffect(() => {
if (!enabled) return;
- const touch = e.touches[0];
- swipeStateRef.current = {
- startX: touch.clientX,
- startY: touch.clientY,
- startTime: Date.now(),
- };
- }, [enabled]);
- const handleTouchEnd = useCallback((e: TouchEvent) => {
- if (!enabled || !swipeStateRef.current) return;
-
- const touch = e.changedTouches[0];
- const { startX, startY, startTime } = swipeStateRef.current;
-
- const deltaX = touch.clientX - startX;
- const deltaY = touch.clientY - startY;
- const deltaTime = Date.now() - startTime;
-
- const absX = Math.abs(deltaX);
- const absY = Math.abs(deltaY);
- const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
- const speed = distance / Math.max(deltaTime, 1);
-
- // If movement is too small, treat as tap
- if (distance < minSwipeDistance) {
- onTap?.();
+ const handleTouchStart = (e: TouchEvent) => {
+ const touch = e.touches[0];
+ swipeStateRef.current = {
+ startX: touch.clientX,
+ startY: touch.clientY,
+ startTime: Date.now(),
+ };
+ };
+
+ const handleTouchEnd = (e: TouchEvent) => {
+ if (!swipeStateRef.current) return;
+
+ const touch = e.changedTouches[0];
+ const { startX, startY, startTime } = swipeStateRef.current;
+
+ const deltaX = touch.clientX - startX;
+ const deltaY = touch.clientY - startY;
+ const deltaTime = Date.now() - startTime;
+
+ const absX = Math.abs(deltaX);
+ const absY = Math.abs(deltaY);
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ const speed = distance / Math.max(deltaTime, 1);
+
+ // If movement is too small, treat as tap
+ if (distance < minSwipeDistance) {
+ onTapRef.current?.();
+ swipeStateRef.current = null;
+ return;
+ }
+
+ const isFastSwipe = speed > fastSwipeThreshold;
+
+ // Determine direction based on larger axis
+ let direction: SwipeDirection;
+ if (absX > absY) {
+ direction = deltaX > 0 ? 'right' : 'left';
+ } else {
+ direction = deltaY > 0 ? 'down' : 'up';
+ }
+
+ onSwipeRef.current(direction, isFastSwipe);
swipeStateRef.current = null;
- return;
- }
-
- const isFastSwipe = speed > fastSwipeThreshold;
-
- // Determine direction based on larger axis
- let direction: SwipeDirection;
- if (absX > absY) {
- direction = deltaX > 0 ? 'right' : 'left';
- } else {
- direction = deltaY > 0 ? 'down' : 'up';
- }
-
- onSwipe(direction, isFastSwipe);
- swipeStateRef.current = null;
- }, [enabled, minSwipeDistance, fastSwipeThreshold, onSwipe, onTap]);
-
- const handleTouchCancel = useCallback(() => {
- swipeStateRef.current = null;
- }, []);
-
- // Returns handlers to attach to an element
- const getSwipeHandlers = useCallback(() => ({
- onTouchStart: (e: React.TouchEvent) => handleTouchStart(e.nativeEvent),
- onTouchEnd: (e: React.TouchEvent) => handleTouchEnd(e.nativeEvent),
- onTouchCancel: () => handleTouchCancel(),
- }), [handleTouchStart, handleTouchEnd, handleTouchCancel]);
-
- // Alternative: attach to a specific element ref
- const bindToElement = useCallback((element: HTMLElement | null) => {
- if (!element) return;
-
- element.addEventListener('touchstart', handleTouchStart, { passive: true });
- element.addEventListener('touchend', handleTouchEnd, { passive: true });
- element.addEventListener('touchcancel', handleTouchCancel, { passive: true });
-
- return () => {
- element.removeEventListener('touchstart', handleTouchStart);
- element.removeEventListener('touchend', handleTouchEnd);
- element.removeEventListener('touchcancel', handleTouchCancel);
};
- }, [handleTouchStart, handleTouchEnd, handleTouchCancel]);
+
+ const handleTouchCancel = () => {
+ swipeStateRef.current = null;
+ };
+
+ // Attach to document for full-screen gesture detection
+ document.addEventListener('touchstart', handleTouchStart, { passive: true });
+ document.addEventListener('touchend', handleTouchEnd, { passive: true });
+ document.addEventListener('touchcancel', handleTouchCancel, { passive: true });
+
+ return () => {
+ document.removeEventListener('touchstart', handleTouchStart);
+ document.removeEventListener('touchend', handleTouchEnd);
+ document.removeEventListener('touchcancel', handleTouchCancel);
+ };
+ }, [enabled, minSwipeDistance, fastSwipeThreshold]);
+
+ // Legacy handlers for specific element binding (not used with document-level)
+ const getSwipeHandlers = useCallback(() => ({}), []);
+ const bindToElement = useCallback(() => () => {}, []);
return { getSwipeHandlers, bindToElement };
};
diff --git a/src/pages/Games.tsx b/src/pages/Games.tsx
index 3b3b052..1855cb4 100644
--- a/src/pages/Games.tsx
+++ b/src/pages/Games.tsx
@@ -20,7 +20,7 @@ const games = [
id: 'pacman',
name: 'Pac-Man',
description: 'Navigate the maze, eat dots, avoid ghosts',
- hasMultiplayer: false,
+ hasMultiplayer: true,
ascii: `┌────────┐
│· · ᗣ · │
│ ┌─┐ ┌─┐│
@@ -44,7 +44,7 @@ const games = [
id: 'breakout',
name: 'Breakout',
description: 'Break bricks with a bouncing ball',
- hasMultiplayer: false,
+ hasMultiplayer: true,
ascii: `┌────────┐
│████████│
│▓▓▓▓▓▓▓▓│
@@ -110,9 +110,9 @@ const Games = () => {
- {/* Multiplayer Badge */}
+ {/* Multiplayer Badge - using primary colors instead of purple */}
{game.hasMultiplayer && (
-
+
2P
)}
@@ -123,17 +123,13 @@ const Games = () => {
{/* Footer */}
-
-
- {'>'} Max score: 4,294,967,296
- (uint32 overflow)
-
-
- Arrow keys / WASD to control
+
+
+ Games auto-save high scores • 2P games require keyboard
);
};
-export default Games;
\ No newline at end of file
+export default Games;