initialize project structure with essential files and configurations from ToneGuessr
This commit is contained in:
parent
569e390a89
commit
e818af6900
33
.github/workflows/deploy_docker.yml
vendored
Normal file
33
.github/workflows/deploy_docker.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
name: Publish Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up docker buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: git.gnm.dev
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GT_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: git.gnm.dev/websiteprojects/liedkampf:main
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -130,3 +130,5 @@ dist
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# editors
|
||||
.idea
|
30
Dockerfile
Normal file
30
Dockerfile
Normal file
@ -0,0 +1,30 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
RUN npm install -g pnpm
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --chown=node:node ./package.json ./pnpm-lock.yaml /app/
|
||||
COPY --chown=node:node ./client/package.json ./client/pnpm-lock.yaml /client/
|
||||
|
||||
WORKDIR /client
|
||||
RUN NODE_ENV=development pnpm install
|
||||
|
||||
COPY --chown=node:node ./server /app
|
||||
COPY --chown=node:node ./client /client
|
||||
|
||||
RUN pnpm run build
|
||||
|
||||
RUN cp -r /client/dist /app/dist
|
||||
|
||||
WORKDIR /app
|
||||
RUN pnpm install --production --frozen-lockfile
|
||||
|
||||
RUN chown -R node:node /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
USER node
|
||||
EXPOSE 5287
|
||||
|
||||
CMD ["node", "index.js"]
|
15
client/.eslintrc.cjs
Normal file
15
client/.eslintrc.cjs
Normal file
@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||
settings: { react: { version: '18.2' } },
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': 'warn',
|
||||
},
|
||||
}
|
24
client/.gitignore
vendored
Normal file
24
client/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
13
client/index.html
Normal file
13
client/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/png" href="/logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ToneGuessr</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
10
client/jsconfig.json
Normal file
10
client/jsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
32
client/package.json
Normal file
32
client/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "webui",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/bangers": "^5.1.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"sass": "^1.85.1",
|
||||
"socket.io-client": "^4.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
3194
client/pnpm-lock.yaml
generated
Normal file
3194
client/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
4
client/public/background.svg
Normal file
4
client/public/background.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z"
|
||||
fill="#ffffff" fill-opacity="0.05" fill-rule="evenodd"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/public/logo.png
Normal file
BIN
client/public/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
61
client/src/App.jsx
Normal file
61
client/src/App.jsx
Normal file
@ -0,0 +1,61 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faDrum, faGuitar, faHeadphones, faMusic} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
const App = () => {
|
||||
const [cursorPos, setCursorPos] = useState({x: 0, y: 0});
|
||||
|
||||
const musicNotes = [
|
||||
{id: 1, top: "8%", left: "20%", icon: faMusic, scale: 1.2},
|
||||
{id: 2, top: "75%", left: "85%", icon: faGuitar, scale: 1},
|
||||
{id: 3, top: "65%", left: "12%", icon: faHeadphones, scale: 1.1},
|
||||
{id: 4, top: "20%", left: "70%", icon: faDrum, scale: 0.9}
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e) => {
|
||||
if (!handleMouseMove.ticking) {
|
||||
handleMouseMove.ticking = true;
|
||||
requestAnimationFrame(() => {
|
||||
setCursorPos({x: e.clientX, y: e.clientY});
|
||||
handleMouseMove.ticking = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
handleMouseMove.ticking = false;
|
||||
|
||||
window.addEventListener('mousemove', handleMouseMove);
|
||||
return () => window.removeEventListener('mousemove', handleMouseMove);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="background-elements">
|
||||
<div className="glow-point point-1" style={{
|
||||
transform: `translate(${(cursorPos.x / window.innerWidth - 0.5) * -20}px, ${(cursorPos.y / window.innerHeight - 0.5) * -20}px)`
|
||||
}}></div>
|
||||
<div className="glow-point point-2" style={{
|
||||
transform: `translate(${(cursorPos.x / window.innerWidth - 0.5) * 15}px, ${(cursorPos.y / window.innerHeight - 0.5) * 15}px)`
|
||||
}}></div>
|
||||
<div className="glow-point point-3" style={{
|
||||
transform: `translate(${(cursorPos.x / window.innerWidth - 0.5) * -10}px, ${(cursorPos.y / window.innerHeight - 0.5) * -10}px)`
|
||||
}}></div>
|
||||
|
||||
{musicNotes.map((note) => (
|
||||
<div key={`note-${note.id}`} className={`music-note note-${note.id}`}
|
||||
style={{
|
||||
top: note.top,
|
||||
left: note.left,
|
||||
fontSize: `${note.scale * 60}pt`,
|
||||
transform: `translate(${(cursorPos.x / window.innerWidth - 0.5) * -5 * note.scale}px, ${(cursorPos.y / window.innerHeight - 0.5) * -5 * note.scale}px)`
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={note.icon}/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App;
|
16
client/src/common/styles/colors.sass
Normal file
16
client/src/common/styles/colors.sass
Normal file
@ -0,0 +1,16 @@
|
||||
$background: rgba(255, 255, 255, 0.14)
|
||||
$border: rgba(255, 255, 255, 0.35)
|
||||
$white: #ECECEC
|
||||
$green: #26EE5E
|
||||
$black: #000
|
||||
$dark-gray: #1e1e1e
|
||||
$light-gray: #aaa
|
||||
$red: #ff0000
|
||||
|
||||
$pink: #ff6bb3
|
||||
$blue: #4d9dff
|
||||
$purple: #9c6bff
|
||||
$cyan: #6bffea
|
||||
$orange: #ff9b6b
|
||||
$yellow: #ffde6b
|
||||
$mint-green: #85ffbd
|
157
client/src/common/styles/forms.sass
Normal file
157
client/src/common/styles/forms.sass
Normal file
@ -0,0 +1,157 @@
|
||||
@import "@/common/styles/colors"
|
||||
|
||||
.form-base
|
||||
background: rgba(255, 255, 255, 0.1)
|
||||
backdrop-filter: blur(15px)
|
||||
border: 1px solid rgba(255, 255, 255, 0.2)
|
||||
border-radius: 20px
|
||||
padding: 2.5rem
|
||||
width: 100%
|
||||
position: relative
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15), 0 0 20px rgba(255, 255, 255, 0.1)
|
||||
|
||||
h2
|
||||
margin: 0 0 2rem
|
||||
text-align: center
|
||||
color: $white
|
||||
font-size: 2.5rem
|
||||
text-shadow: 0 0 10px rgba(255, 255, 255, 0.4)
|
||||
|
||||
form
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 1.5rem
|
||||
|
||||
.input-group
|
||||
position: relative
|
||||
|
||||
input
|
||||
width: 100%
|
||||
padding: 1.2rem 1.5rem
|
||||
background: rgba(255, 255, 255, 0.07)
|
||||
border: 1px solid rgba(255, 255, 255, 0.1)
|
||||
border-radius: 12px
|
||||
color: $white
|
||||
font-family: 'Bangers', sans-serif
|
||||
font-size: 1.2rem
|
||||
letter-spacing: 0.15rem
|
||||
transition: all 0.3s ease
|
||||
outline: none
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1)
|
||||
|
||||
&::placeholder
|
||||
color: rgba(255, 255, 255, 0.5)
|
||||
|
||||
&:focus
|
||||
border-color: rgba(255, 255, 255, 0.5)
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15), 0 0 10px rgba(255, 255, 255, 0.2)
|
||||
|
||||
&.error
|
||||
border-color: rgba(255, 0, 0, 0.5)
|
||||
animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both
|
||||
|
||||
.error-message
|
||||
position: absolute
|
||||
color: rgba(255, 100, 100, 0.9)
|
||||
font-size: 0.85rem
|
||||
bottom: -1.2rem
|
||||
left: 0.2rem
|
||||
animation: fade-in 0.3s ease
|
||||
|
||||
.button
|
||||
border: none
|
||||
border-radius: 12px
|
||||
padding: 1.2rem
|
||||
color: $white
|
||||
font-family: 'Bangers', sans-serif
|
||||
font-size: 1.3rem
|
||||
letter-spacing: 0.15rem
|
||||
cursor: pointer
|
||||
transition: all 0.3s ease
|
||||
display: flex
|
||||
justify-content: center
|
||||
align-items: center
|
||||
gap: 0.8rem
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15)
|
||||
|
||||
&:hover
|
||||
transform: translateY(-3px)
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2)
|
||||
|
||||
&:active
|
||||
transform: translateY(0)
|
||||
|
||||
.submit-button
|
||||
@extend .button
|
||||
margin-top: 1rem
|
||||
|
||||
&.join-button
|
||||
background: linear-gradient(45deg, $blue, $purple)
|
||||
|
||||
&:hover
|
||||
background: linear-gradient(45deg, lighten($blue, 10%), lighten($purple, 10%))
|
||||
|
||||
&.create-button
|
||||
background: linear-gradient(45deg, $pink, $purple)
|
||||
|
||||
&:hover
|
||||
background: linear-gradient(45deg, lighten($pink, 10%), lighten($purple, 10%))
|
||||
|
||||
.back-button
|
||||
position: absolute
|
||||
top: -4rem
|
||||
left: 0
|
||||
background: rgba(255, 255, 255, 0.1)
|
||||
border: 1px solid rgba(255, 255, 255, 0.2)
|
||||
border-radius: 10px
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
gap: 0.7rem
|
||||
color: rgba(255, 255, 255, 0.9)
|
||||
cursor: pointer
|
||||
font-family: 'Bangers', sans-serif
|
||||
font-size: 1.1rem
|
||||
padding: 0.7rem 1.3rem
|
||||
transition: all 0.3s ease
|
||||
letter-spacing: 0.1rem
|
||||
backdrop-filter: blur(10px)
|
||||
|
||||
svg
|
||||
font-size: 1rem
|
||||
transition: transform 0.3s ease
|
||||
|
||||
&:hover
|
||||
color: $white
|
||||
background: rgba(255, 255, 255, 0.2)
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1)
|
||||
|
||||
svg
|
||||
transform: translateX(-5px)
|
||||
|
||||
.glassy-card
|
||||
background: rgba(255, 255, 255, 0.07)
|
||||
backdrop-filter: blur(10px)
|
||||
border: 1px solid rgba(255, 255, 255, 0.2)
|
||||
border-radius: 20px
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1), 0 0 20px rgba(255, 255, 255, 0.1)
|
||||
overflow: hidden
|
||||
transition: all 0.3s ease
|
||||
|
||||
@keyframes shake
|
||||
10%, 90%
|
||||
transform: translate3d(-1px, 0, 0)
|
||||
20%, 80%
|
||||
transform: translate3d(2px, 0, 0)
|
||||
30%, 50%, 70%
|
||||
transform: translate3d(-4px, 0, 0)
|
||||
40%, 60%
|
||||
transform: translate3d(4px, 0, 0)
|
||||
|
||||
@keyframes fade-in
|
||||
from
|
||||
opacity: 0
|
||||
transform: translateY(-5px)
|
||||
to
|
||||
opacity: 1
|
||||
transform: translateY(0)
|
230
client/src/common/styles/main.sass
Normal file
230
client/src/common/styles/main.sass
Normal file
@ -0,0 +1,230 @@
|
||||
@use "@/common/styles/colors" as *
|
||||
|
||||
*
|
||||
box-sizing: border-box
|
||||
|
||||
body
|
||||
margin: 0
|
||||
padding: 0
|
||||
letter-spacing: 0.2rem
|
||||
color: #E0E0E0
|
||||
font-family: 'Bangers', sans-serif
|
||||
height: 100vh
|
||||
width: 100vw
|
||||
background-size: 300% 300%
|
||||
animation: shifting-background 30s ease infinite
|
||||
background: linear-gradient(45deg, #4d3ae7 0%, #b84bc3 50%, #ff6a88 100%) fixed
|
||||
user-select: none
|
||||
overflow: hidden
|
||||
position: relative
|
||||
|
||||
&:before
|
||||
content: ""
|
||||
position: fixed
|
||||
top: 0
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
background-image: url("/background.svg")
|
||||
|
||||
.background-overlay
|
||||
position: fixed
|
||||
top: 0
|
||||
left: 0
|
||||
width: 100vw
|
||||
height: 100vh
|
||||
z-index: 0
|
||||
pointer-events: none
|
||||
|
||||
.rotating-gradient
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
width: 250vh
|
||||
height: 250vh
|
||||
transform: translate(-50%, -50%)
|
||||
background: conic-gradient(from 0deg, rgba(255, 102, 196, 0.2), rgba(102, 204, 255, 0.2), rgba(255, 209, 128, 0.2), rgba(133, 255, 189, 0.2), rgba(255, 102, 196, 0.2))
|
||||
border-radius: 50%
|
||||
animation: rotate-background 180s linear infinite
|
||||
will-change: transform
|
||||
transform-origin: center center
|
||||
opacity: 0.7
|
||||
filter: blur(40px)
|
||||
|
||||
&:after
|
||||
content: ""
|
||||
position: fixed
|
||||
top: 0
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
background: radial-gradient(circle at center, transparent 0%, rgba(0, 0, 0, 0.1) 60%, rgba(0, 0, 0, 0.4) 100%)
|
||||
z-index: 1
|
||||
pointer-events: none
|
||||
|
||||
::-webkit-scrollbar
|
||||
width: 8px
|
||||
background-color: rgba(0, 0, 0, 0.2)
|
||||
border-radius: 4px
|
||||
|
||||
::-webkit-scrollbar-thumb
|
||||
background-color: rgba(255, 255, 255, 0.15)
|
||||
border-radius: 4px
|
||||
border: 1px solid rgba(255, 255, 255, 0.05)
|
||||
|
||||
&:hover
|
||||
background-color: rgba(255, 255, 255, 0.25)
|
||||
|
||||
.glassy
|
||||
background: $background
|
||||
backdrop-filter: blur(10px)
|
||||
border: 2px solid $border
|
||||
border-radius: 0.8rem
|
||||
|
||||
.background-elements
|
||||
position: fixed
|
||||
top: 0
|
||||
left: 0
|
||||
width: 100vw
|
||||
height: 100vh
|
||||
overflow: hidden
|
||||
z-index: 2
|
||||
pointer-events: none
|
||||
|
||||
.glow-point
|
||||
position: absolute
|
||||
width: 250px
|
||||
height: 250px
|
||||
border-radius: 50%
|
||||
filter: blur(100px)
|
||||
opacity: 0.4
|
||||
will-change: transform
|
||||
transform: translateZ(0)
|
||||
|
||||
&.point-1
|
||||
top: 20%
|
||||
left: 10%
|
||||
background-color: rgba(255, 102, 196, 0.8)
|
||||
animation: float-glow 15s infinite alternate ease-in-out
|
||||
|
||||
&.point-2
|
||||
top: 70%
|
||||
left: 80%
|
||||
background-color: rgba(102, 204, 255, 0.8)
|
||||
animation: float-glow 18s infinite alternate-reverse ease-in-out
|
||||
|
||||
&.point-3
|
||||
top: 80%
|
||||
left: 20%
|
||||
background-color: rgba(133, 255, 189, 0.8)
|
||||
animation: float-glow 12s infinite alternate ease-in-out 2s
|
||||
|
||||
.music-note
|
||||
position: absolute
|
||||
font-size: 60pt
|
||||
opacity: 0.7
|
||||
will-change: transform, opacity, filter
|
||||
filter: drop-shadow(0 0 15px rgba(255, 255, 255, 0.7))
|
||||
transform: translateZ(0)
|
||||
backface-visibility: hidden
|
||||
|
||||
@for $i from 1 through 5
|
||||
&.note-#{$i}
|
||||
animation-name: float-note-#{$i}, pulse-note
|
||||
animation-duration: #{10 + ($i * 1)}s, 5s
|
||||
animation-timing-function: ease-in-out, ease-in-out
|
||||
animation-iteration-count: infinite, infinite
|
||||
animation-direction: alternate, alternate
|
||||
animation-delay: #{$i * 0.7}s, #{$i * 0.4}s
|
||||
|
||||
@if $i % 4 == 0
|
||||
color: rgba(255, 102, 196, 0.8)
|
||||
@else if $i % 4 == 1
|
||||
color: rgba(102, 204, 255, 0.8)
|
||||
@else if $i % 4 == 2
|
||||
color: rgba(255, 209, 128, 0.8)
|
||||
@else
|
||||
color: rgba(133, 255, 189, 0.8)
|
||||
|
||||
.card-element
|
||||
background: rgba(255, 255, 255, 0.07)
|
||||
backdrop-filter: blur(10px)
|
||||
border: 1px solid rgba(255, 255, 255, 0.2)
|
||||
border-radius: 20px
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1), 0 0 20px rgba(255, 255, 255, 0.1)
|
||||
overflow: hidden
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
animation: card-float 6s infinite ease-in-out alternate
|
||||
|
||||
&:hover
|
||||
transform: translateY(-10px) scale(1.02)
|
||||
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.2), 0 0 40px rgba(255, 255, 255, 0.2)
|
||||
border: 1px solid rgba(255, 255, 255, 0.4)
|
||||
|
||||
@keyframes shifting-background
|
||||
0%
|
||||
background-position: 0% 0%
|
||||
50%
|
||||
background-position: 100% 100%
|
||||
100%
|
||||
background-position: 0% 0%
|
||||
|
||||
@keyframes rotate-background
|
||||
0%
|
||||
transform: translate(-50%, -50%) rotate(0deg)
|
||||
100%
|
||||
transform: translate(-50%, -50%) rotate(360deg)
|
||||
|
||||
@keyframes float-glow
|
||||
0%, 100%
|
||||
transform: translate(0, 0) scale(1)
|
||||
filter: blur(100px)
|
||||
50%
|
||||
transform: translate(30px, -20px) scale(1.2)
|
||||
filter: blur(80px)
|
||||
|
||||
@keyframes card-float
|
||||
0%, 100%
|
||||
transform: translateY(0)
|
||||
50%
|
||||
transform: translateY(-8px)
|
||||
|
||||
@keyframes float-note-1
|
||||
0%, 100%
|
||||
transform: translateY(0) rotate(0deg)
|
||||
50%
|
||||
transform: translateY(-40px) rotate(10deg)
|
||||
|
||||
@keyframes float-note-2
|
||||
0%, 100%
|
||||
transform: translateY(0) rotate(0deg)
|
||||
50%
|
||||
transform: translateY(-30px) rotate(-8deg)
|
||||
|
||||
@keyframes float-note-3
|
||||
0%, 100%
|
||||
transform: translateY(0) rotate(0deg)
|
||||
50%
|
||||
transform: translateY(-50px) rotate(15deg)
|
||||
|
||||
@keyframes float-note-4
|
||||
0%, 100%
|
||||
transform: translateY(0) rotate(0deg)
|
||||
33%
|
||||
transform: translateY(-25px) rotate(-5deg)
|
||||
66%
|
||||
transform: translateY(-40px) rotate(5deg)
|
||||
|
||||
@keyframes float-note-5
|
||||
0%, 100%
|
||||
transform: translateY(0) rotate(0deg)
|
||||
50%
|
||||
transform: translateY(-35px) rotate(-12deg)
|
||||
|
||||
@keyframes pulse-note
|
||||
0%, 100%
|
||||
filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.5))
|
||||
opacity: 0.6
|
||||
50%
|
||||
filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.7))
|
||||
opacity: 0.9
|
15
client/src/main.jsx
Normal file
15
client/src/main.jsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App.jsx";
|
||||
import "@fontsource/bangers";
|
||||
import "@/common/styles/main.sass";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||
<React.StrictMode>
|
||||
|
||||
<div className="app">
|
||||
<App/>
|
||||
</div>
|
||||
|
||||
</React.StrictMode>,
|
||||
);
|
21
client/vite.config.js
Normal file
21
client/vite.config.js
Normal file
@ -0,0 +1,21 @@
|
||||
import {defineConfig} from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import * as path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
"/socket.io": {
|
||||
target: "http://localhost:5287",
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
23
package.json
Normal file
23
package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "toneguessr",
|
||||
"version": "1.0.0-ALPHA",
|
||||
"description": "The server of the ToneGuessr game",
|
||||
"main": "server/index.js",
|
||||
"repository": "https://git.gnm.dev/WebsiteProjects/ToneGuessr",
|
||||
"author": "Mathias Wagner",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"webui": "cd client && yarn dev",
|
||||
"server": "nodemon server",
|
||||
"dev": "concurrently --kill-others \"yarn server\" \"yarn webui\""
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.21.2",
|
||||
"googleapis": "^146.0.0",
|
||||
"socket.io": "^4.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^9.1.2",
|
||||
"nodemon": "^3.1.9"
|
||||
}
|
||||
}
|
1358
pnpm-lock.yaml
generated
Normal file
1358
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
server/index.js
Normal file
35
server/index.js
Normal file
@ -0,0 +1,35 @@
|
||||
const express = require("express");
|
||||
const { Server } = require("socket.io");
|
||||
const http = require("http");
|
||||
const app = express();
|
||||
const path = require("path");
|
||||
|
||||
app.use(express.static(path.join(__dirname, './dist')));
|
||||
app.disable("x-powered-by");
|
||||
|
||||
app.get('*', (req, res) => res.sendFile(path.join(__dirname, './dist', 'index.html')));
|
||||
|
||||
const server = http.createServer(app);
|
||||
|
||||
const io = new Server(server, {
|
||||
cors: {origin: "*"},
|
||||
pingTimeout: 30000,
|
||||
pingInterval: 10000
|
||||
});
|
||||
|
||||
server.on('error', (error) => {
|
||||
console.error('Server error:', error);
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 5237;
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
console.log('Server shutting down...');
|
||||
server.close(() => {
|
||||
console.log('Server closed');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user