import React, { useState, useEffect, useRef, useCallback } from 'react';
import { Settings, ShoppingBag, Volume2, Users, User, Home, Power } from 'lucide-react'; // Importing Home and Power icons
// Main App component for the Aura X Game Console UI
const App = () => {
// State to manage the active menu item for navigation
const [activeMenuItem, setActiveMenuItem] = useState('Home'); // Default to Home
// State to manage the currently focused element for gamepad/keyboard navigation
const [focusedElement, setFocusedElement] = useState({ type: 'menu', id: 'Home' }); // Default focus to Home
// State to trigger icon animations on press
const [animatingIconId, setAnimatingIconId] = useState(null);
// Refs for menu items and game cards to allow programmatic focus
const menuRefs = useRef({});
const gameRefs = useRef({});
const gameListContainerRef = useRef(null); // Ref for the scrollable game list container
// Dummy data for menu items with icons and specific colors
const menuItems = [
{ id: 'Home', name: 'Home', icon: Home, defaultIconColor: 'text-gray-700' },
{ id: 'Settings', name: 'Settings', icon: Settings, defaultIconColor: 'text-gray-700' },
{ id: 'Store', name: 'Store', icon: ShoppingBag, defaultIconColor: 'text-gray-700' },
{ id: 'Friends', name: 'Friends', icon: Users, defaultIconColor: 'text-[#FF0000]' }, // Red for Users
{ id: 'Profile', name: 'Profile', icon: User, defaultIconColor: 'text-[#00CC00]' }, // Green for User
{ id: 'Power', name: 'Power', icon: Power, defaultIconColor: 'text-blue-500' }, // Power at the bottom
];
// Dummy data for game titles to display, matching image proportions
// Expanded to more than 9 to demonstrate scrolling
const games = [
{ id: 1, title: 'LEGO Star Wars', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=LEGO+SW', border: 'border-blue-400' }, // Example from image
{ id: 2, title: 'Fortnite', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=Fortnite', border: 'border-blue-400' }, // Example from image
{ id: 3, title: 'Cyberpunk 2077', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=Cyberpunk' },
{ id: 4, title: 'The Witcher 3', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=Witcher+3' },
{ id: 5, title: 'Red Dead Redemption 2', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=RDR2' },
{ id: 6, title: 'God of War', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=God+of+War' },
{ id: 7, title: 'Spider-Man: Miles Morales', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=Spider-Man' },
{ id: 8, title: 'Horizon Zero Dawn', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=Horizon' },
{ id: 9, title: 'Elden Ring', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=Elden+Ring' },
{ id: 10, title: 'Doom Eternal', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=Doom' },
{ id: 11, title: 'Halo Infinite', icon: 'https://placehold.co/200x260/000000/FFFFFF?text=Halo' },
];
// Function to get current time for the header
const getCurrentTime = () => {
const now = new Date();
return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: true });
};
const [currentTime, setCurrentTime] = useState(getCurrentTime());
useEffect(() => {
const timer = setInterval(() => {
setCurrentTime(getCurrentTime());
}, 60000); // Update every minute
return () => clearInterval(timer);
}, []);
// Effect to handle gamepad connection and disconnection
useEffect(() => {
const handleGamepadConnected = (e) => {
console.log('Gamepad connected at index %d: %s. %d buttons, %d axes.',
e.gamepad.index, e.gamepad.id,
e.gamepad.buttons.length, e.gamepad.axes.length);
setGamepad(e.gamepad);
// Set initial focus to the Home menu item when gamepad connects
setFocusedElement({ type: 'menu', id: 'Home' });
};
const handleGamepadDisconnected = (e) => {
console.log('Gamepad disconnected from index %d: %s',
e.gamepad.index, e.gamepad.id);
setGamepad(null);
setFocusedElement({ type: 'none', id: null }); // Clear focus
};
window.addEventListener('gamepadconnected', handleGamepadConnected);
window.addEventListener('gamepaddisconnected', handleGamepadDisconnected);
// Check for already connected gamepads on mount
const gamepads = navigator.getGamepads();
if (gamepads[0]) {
setGamepad(gamepads[0]);
setFocusedElement({ type: 'menu', id: 'Home' });
}
return () => {
window.removeEventListener('gamepadconnected', handleGamepadConnected);
window.removeEventListener('gamepaddisconnected', handleGamepadDisconnected);
};
}, []);
// Debounce state for gamepad/keyboard buttons to prevent multiple triggers on a single press
const [buttonStates, setButtonStates] = useState({});
// Unified navigation logic for both gamepad and keyboard
const navigate = useCallback((direction) => {
let handled = false;
if (focusedElement.type === 'menu') {
const currentIndex = menuItems.findIndex(item => item.id === focusedElement.id);
let nextIndex;
if (direction === 'up') {
nextIndex = (currentIndex - 1 + menuItems.length) % menuItems.length;
setFocusedElement({ type: 'menu', id: menuItems[nextIndex].id });
handled = true;
} else if (direction === 'down') {
nextIndex = (currentIndex + 1) % menuItems.length;
setFocusedElement({ type: 'menu', id: menuItems[nextIndex].id });
handled = true;
} else if (direction === 'right') {
// Only move to games if 'Home' is the active menu (since Home now shows games)
if (activeMenuItem === 'Home' && games.length > 0) {
setFocusedElement({ type: 'game', id: games[0].id });
handled = true;
}
}
} else if (focusedElement.type === 'game') {
const allSlots = Array.from({ length: 9 }).map((_, i) => games[i] ? games[i].id : `empty-${i}`);
const currentIndex = allSlots.findIndex(id => id === focusedElement.id);
let nextIndex;
if (direction === 'up') {
nextIndex = (currentIndex - 1 + allSlots.length) % allSlots.length;
setFocusedElement({ type: 'game', id: allSlots[nextIndex] });
handled = true;
} else if (direction === 'down') {
nextIndex = (currentIndex + 1) % allSlots.length;
setFocusedElement({ type: 'game', id: allSlots[nextIndex] });
handled = true;
} else if (direction === 'left') {
// Move from games to menu, setting activeMenuItem to 'Home' (since Home shows games)
setFocusedElement({ type: 'menu', id: 'Home' });
handled = true;
}
}
return handled; // Return true if navigation was handled
}, [focusedElement, activeMenuItem, games, menuItems]);
// Function to handle gamepad input polling
const handleGamepadInput = useCallback(() => {
if (!gamepad) return;
const currentGamepad = navigator.getGamepads()[gamepad.index];
if (!currentGamepad) {
setGamepad(null); // Gamepad might have disconnected
return;
}
const newButtonStates = {};
for (let i = 0; i < currentGamepad.buttons.length; i++) {
const button = currentGamepad.buttons[i];
const isPressed = button.pressed;
newButtonStates[i] = isPressed;
if (isPressed && !buttonStates[i]) {
// A button (Xbox layout)
if (i === 0) {
if (focusedElement.type === 'menu') {
setAnimatingIconId(focusedElement.id); // Trigger animation
setTimeout(() => {
setActiveMenuItem(focusedElement.id); // Set active after animation starts
setAnimatingIconId(null); // Clear animation state
// If 'Home' is selected, and we're moving from menu to games, set focus to first game
if (focusedElement.id === 'Home' && games.length > 0) {
setFocusedElement({ type: 'game', id: games[0].id });
}
}, 300); // Match animation duration
} else if (focusedElement.type === 'game') {
console.log(`Playing game: ${focusedElement.title}`);
// Implement actual game launch logic here
}
}
// D-pad or left stick for navigation
else if (i === 12 || currentGamepad.axes[1] < -0.5) { // Up
navigate('up');
} else if (i === 13 || currentGamepad.axes[1] > 0.5) { // Down
navigate('down');
} else if (i === 14 || currentGamepad.axes[0] < -0.5) { // Left
navigate('left');
} else if (i === 15 || currentGamepad.axes[0] > 0.5) { // Right
navigate('right');
}
}
}
setButtonStates(newButtonStates);
}, [gamepad, focusedElement, navigate, games, buttonStates]);
// Keyboard input handler
const handleKeyDown = useCallback((event) => {
let handled = false;
switch (event.key) {
case 'ArrowUp':
handled = navigate('up');
break;
case 'ArrowDown':
handled = navigate('down');
break;
case 'ArrowLeft':
handled = navigate('left');
break;
case 'ArrowRight':
handled = navigate('right');
break;
case 'Enter': // Confirmation key
case 'z': // 'Z' key for confirmation
case 'Z': // 'Z' key for confirmation (uppercase)
if (focusedElement.type === 'menu') {
setAnimatingIconId(focusedElement.id); // Trigger animation
setTimeout(() => {
setActiveMenuItem(focusedElement.id); // Set active after animation starts
setAnimatingIconId(null); // Clear animation state
// If 'Home' is selected, and we're moving from menu to games, set focus to first game
if (focusedElement.id === 'Home' && games.length > 0) {
setFocusedElement({ type: 'game', id: games[0].id });
}
}, 300); // Match animation duration
} else if (focusedElement.type === 'game') {
console.log(`Playing game: ${focusedElement.title}`);
}
handled = true;
break;
default:
break;
}
if (handled) {
event.preventDefault(); // Prevent default browser scrolling/behavior
}
}, [focusedElement, navigate, games]);
// Animation loop for gamepad polling
useEffect(() => {
let animationFrameId;
const gameLoop = () => {
handleGamepadInput();
animationFrameId = requestAnimationFrame(gameLoop);
};
if (gamepad) {
animationFrameId = requestAnimationFrame(gameLoop);
}
return () => {
cancelAnimationFrame(animationFrameId);
};
}, [gamepad, handleGamepadInput]);
// Add keyboard event listener
useEffect(() => {
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [handleKeyDown]);
// Effect to scroll focused game element into view
useEffect(() => {
if (focusedElement.type === 'menu' && menuRefs.current[focusedElement.id]) {
menuRefs.current[focusedElement.id].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} else if (focusedElement.type === 'game' && gameRefs.current[focusedElement.id]) {
// Scroll the specific game card into view within its container
gameRefs.current[focusedElement.id].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}, [focusedElement]);
return (
// Main container for the app, full screen height and width, dark background matching image
{/* Console Header */}
{/* Avatar matching image style */}
Hello, TheRedSlimeYT!
{currentTime}
{/* Main Content Area: Sidebar + Content */}
{/* Side Navigation Menu */}
{/* Add keyframes for the gradient and icon animations */}
{/* Content Display Area */}
{/* Removed justify-center items-center */}
{/* Display games if Home is active, matching the image */}
{activeMenuItem === 'Home' && (