Default cursor is not being displayed on top of the custom one upon

exiting fullscreen
Added package-lock file so running code does not contain any
vulnerabilities anymore
Snake game also dynamically changes sized when fullscreen just like the
other games do
Invert glow effect colour on Snake as it had broken logic
This commit is contained in:
2026-02-03 14:35:21 +01:00
parent c0c73f9e44
commit 4f2fd37e29
5 changed files with 44 additions and 34 deletions
+18 -18
View File
@@ -2342,9 +2342,9 @@
"license": "MIT"
},
"node_modules/@remix-run/router": {
"version": "1.23.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
"integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==",
"version": "1.23.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
"integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
@@ -6042,9 +6042,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT"
},
"node_modules/lodash.castarray": {
@@ -6977,9 +6977,9 @@
}
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -7157,12 +7157,12 @@
}
},
"node_modules/react-router": {
"version": "6.30.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz",
"integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==",
"version": "6.30.3",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
"integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.23.0"
"@remix-run/router": "1.23.2"
},
"engines": {
"node": ">=14.0.0"
@@ -7172,13 +7172,13 @@
}
},
"node_modules/react-router-dom": {
"version": "6.30.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz",
"integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==",
"version": "6.30.3",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
"integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.23.0",
"react-router": "6.30.1"
"@remix-run/router": "1.23.2",
"react-router": "6.30.3"
},
"engines": {
"node": ">=14.0.0"
+6
View File
@@ -74,7 +74,13 @@ const MatrixCursor = () => {
document.removeEventListener('mouseenter', handleMouseEnter);
document.removeEventListener('mouseleave', handleMouseLeave);
document.removeEventListener('touchstart', handleTouch);
// Only remove cursor class if no other MatrixCursor components are mounted
// This prevents the cursor from showing when exiting fullscreen
const otherCursors = document.querySelectorAll('.matrix-cursor-crosshair');
if (otherCursors.length === 0) {
document.body.classList.remove('matrix-cursor');
}
};
}, [isTouchDevice]);
+5 -1
View File
@@ -3,6 +3,7 @@ import { motion, AnimatePresence } from 'framer-motion';
import { Settings, Monitor, Volume2, Cpu, X, Contrast, Binary, Smartphone } from 'lucide-react';
import { useSettings, MobileControlMode } from '@/contexts/SettingsContext';
import { useAchievements } from '@/contexts/AchievementsContext';
import { useIsMobile } from '@/hooks/use-mobile';
import CryptoConsentModal from './CryptoConsentModal';
interface SettingsPanelProps {
@@ -15,6 +16,7 @@ const SettingsPanel = ({ onToggleTheme, isRedTheme }: SettingsPanelProps) => {
const [showCryptoModal, setShowCryptoModal] = useState(false);
const { crtEnabled, setCrtEnabled, matrixEnabled, setMatrixEnabled, soundEnabled, setSoundEnabled, cryptoConsent, playSound, mobileControlMode, setMobileControlMode } = useSettings();
const { unlockAchievement } = useAchievements();
const isMobile = useIsMobile();
const handleToggle = (setter: (value: boolean) => void, currentValue: boolean, achievementId?: string) => {
playSound('click');
@@ -156,10 +158,11 @@ const SettingsPanel = ({ onToggleTheme, isRedTheme }: SettingsPanelProps) => {
</div>
{/* Mobile Controls Mode */}
{isMobile && (
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Smartphone size={16} className="text-primary" />
<span className="font-pixel text-sm text-foreground/90">Mobile Controls</span>
<span className="font-pixel text-sm text-foreground/90">Game Controls (for touch devices)</span>
</div>
<button
onClick={handleMobileControlToggle}
@@ -168,6 +171,7 @@ const SettingsPanel = ({ onToggleTheme, isRedTheme }: SettingsPanelProps) => {
{mobileControlMode === 'buttons' ? 'BUTTONS' : 'SWIPE'}
</button>
</div>
)}
</div>
<div className="mt-4 pt-4 border-t border-primary/30">
+1 -1
View File
@@ -568,7 +568,7 @@ const Snake = () => {
<div className="flex items-center gap-4">
<button onClick={() => { setGameMode(null); setGameStarted(false); }} className="font-pixel text-sm text-foreground/50 hover:text-primary transition-colors">{'<'} Back</button>
<h1 className="font-minecraft text-2xl md:text-3xl text-primary text-glow-strong">
Snake {gameMode === '2p' && <span className="text-secondary">2P</span>}
Snake {gameMode === '2p' && <span className="text-secondary" style={{ textShadow: '0 0 10px hsl(var(--secondary) / 0.8), 0 0 20px hsl(var(--secondary) / 0.4)' }}>2P</span>}
</h1>
</div>
<button onClick={toggleFullscreen} className="p-2 border border-primary/50 hover:bg-primary/20 transition-colors" title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}>
+2 -2
View File
@@ -138,7 +138,7 @@ const Tetris = () => {
const availableHeight = window.innerHeight - 120;
const cellFromWidth = Math.floor(availableWidth / (gameMode === '2p' ? BOARD_WIDTH * 2 + 4 : BOARD_WIDTH));
const cellFromHeight = Math.floor(availableHeight / BOARD_HEIGHT);
return Math.min(cellFromWidth, cellFromHeight, gameMode === '2p' ? 32 : 40);
return Math.min(cellFromWidth, cellFromHeight);
}
if (gameMode === '2p') return 22;
return 28;
@@ -508,7 +508,7 @@ const Tetris = () => {
{nextP.shape.map((row, y) => (
<div key={y} className="flex gap-1">
{row.map((val, x) => (
<div key={`${y}-${x}`} className={`w-4 h-4`} style={{ backgroundColor: val ? nextP.color : 'transparent', boxShadow: val ? `0 0 5px ${nextP.color}` : 'none' }} />
<div key={`${y}-${x}`} style={{ width: cellSize * 0.6, height: cellSize * 0.6, backgroundColor: val ? nextP.color : 'transparent', boxShadow: val ? `0 0 5px ${nextP.color}` : 'none' }} />
))}
</div>
))}