Compare commits
4 Commits
b7306d362c
...
ff706e056d
| Author | SHA1 | Date | |
|---|---|---|---|
| ff706e056d | |||
| 2c472fec5c | |||
| 3c2368d886 | |||
| d0bd636a43 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -43,3 +43,4 @@ next-env.d.ts
|
||||
.dev.vars*
|
||||
!.dev.vars.example
|
||||
!.env.example
|
||||
.specstory/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
367
package-lock.json
generated
367
package-lock.json
generated
@ -9,10 +9,12 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@opennextjs/cloudflare": "^1.3.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.14",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^12.23.24",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lucide-react": "^0.552.0",
|
||||
"minecraft-server-util": "^5.4.4",
|
||||
"motion": "^12.23.24",
|
||||
@ -9460,6 +9462,38 @@
|
||||
"integrity": "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/react-collection": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
|
||||
"integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
@ -9475,6 +9509,164 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-direction": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
||||
"integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dismissable-layer": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
||||
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-escape-keydown": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-id": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
||||
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-navigation-menu": {
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.14.tgz",
|
||||
"integrity": "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-collection": "1.1.7",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-direction": "1.1.1",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.11",
|
||||
"@radix-ui/react-id": "1.1.1",
|
||||
"@radix-ui/react-presence": "1.1.5",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1",
|
||||
"@radix-ui/react-use-previous": "1.1.1",
|
||||
"@radix-ui/react-visually-hidden": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
||||
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
@ -9493,6 +9685,129 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-effect-event": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
|
||||
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-escape-keydown": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
|
||||
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-previous": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
|
||||
"integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-visually-hidden": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
|
||||
"integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||
@ -11354,8 +11669,9 @@
|
||||
"version": "19.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
|
||||
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@ -12294,6 +12610,15 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/blake3-wasm": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
|
||||
@ -12747,6 +13072,15 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/css-line-break": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
@ -14702,6 +15036,19 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/html2canvas": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"css-line-break": "^2.1.0",
|
||||
"text-segmentation": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
@ -18202,6 +18549,15 @@
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/text-segmentation": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
@ -18544,6 +18900,15 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utrie": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||
|
||||
@ -13,10 +13,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@opennextjs/cloudflare": "^1.3.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.14",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^12.23.24",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lucide-react": "^0.552.0",
|
||||
"minecraft-server-util": "^5.4.4",
|
||||
"motion": "^12.23.24",
|
||||
|
||||
301
pnpm-lock.yaml
generated
301
pnpm-lock.yaml
generated
@ -11,6 +11,9 @@ importers:
|
||||
'@opennextjs/cloudflare':
|
||||
specifier: ^1.3.0
|
||||
version: 1.11.0(wrangler@4.45.3)
|
||||
'@radix-ui/react-navigation-menu':
|
||||
specifier: ^1.2.14
|
||||
version: 1.2.14(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-slot':
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.3(@types/react@19.2.2)(react@19.1.0)
|
||||
@ -1092,6 +1095,22 @@ packages:
|
||||
'@poppinss/exception@1.2.2':
|
||||
resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==}
|
||||
|
||||
'@radix-ui/primitive@1.1.3':
|
||||
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
|
||||
|
||||
'@radix-ui/react-collection@1.1.7':
|
||||
resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-compose-refs@1.1.2':
|
||||
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
|
||||
peerDependencies:
|
||||
@ -1101,6 +1120,85 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-context@1.1.2':
|
||||
resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-direction@1.1.1':
|
||||
resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.1.11':
|
||||
resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-id@1.1.1':
|
||||
resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-navigation-menu@1.2.14':
|
||||
resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-presence@1.1.5':
|
||||
resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-primitive@2.1.3':
|
||||
resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-slot@1.2.3':
|
||||
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
|
||||
peerDependencies:
|
||||
@ -1110,6 +1208,73 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.1.1':
|
||||
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-controllable-state@1.2.2':
|
||||
resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-effect-event@0.0.2':
|
||||
resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-escape-keydown@1.1.1':
|
||||
resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-layout-effect@1.1.1':
|
||||
resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-previous@1.1.1':
|
||||
resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-visually-hidden@1.2.3':
|
||||
resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@rtsao/scc@1.1.0':
|
||||
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
||||
|
||||
@ -5380,12 +5545,99 @@ snapshots:
|
||||
|
||||
'@poppinss/exception@1.2.2': {}
|
||||
|
||||
'@radix-ui/primitive@1.1.3': {}
|
||||
|
||||
'@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-direction@1.1.1(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-id@1.1.1(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0)
|
||||
@ -5393,6 +5645,55 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.1.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-previous@1.1.1(@types/react@19.2.2)(react@19.1.0)':
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@rtsao/scc@1.1.0': {}
|
||||
|
||||
'@rushstack/eslint-patch@1.14.1': {}
|
||||
|
||||
BIN
public/backgroundimg-dark.avif
Normal file
BIN
public/backgroundimg-dark.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
public/lever-down.png
Normal file
BIN
public/lever-down.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
public/lever-up.png
Normal file
BIN
public/lever-up.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@ -18,8 +18,6 @@ interface PlanPlayersResponse {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
// Extract player name from HTML string
|
||||
function extractPlayerName(nameString: string): string {
|
||||
// Name format: '<a class="link" href="../player/...">PlayerName</a>'
|
||||
@ -32,9 +30,14 @@ export async function GET() {
|
||||
const playersResponse = await fetch(
|
||||
`${PLAN_BASE_URL}/v1/players?server=${SERVER_NAME}`,
|
||||
{
|
||||
next: { revalidate: 300 }, // Cache for 5 minutes
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'Mozilla/5.0 (compatible; BiohazardVFX/1.0)',
|
||||
'Referer': PLAN_BASE_URL,
|
||||
},
|
||||
cf: {
|
||||
cacheTtl: 300,
|
||||
cacheEverything: true,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@ -25,9 +25,14 @@ export async function GET() {
|
||||
const overviewResponse = await fetch(
|
||||
`${PLAN_BASE_URL}/v1/serverOverview?server=${SERVER_NAME}`,
|
||||
{
|
||||
next: { revalidate: 30 }, // Cache for 30 seconds
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'Mozilla/5.0 (compatible; BiohazardVFX/1.0)',
|
||||
'Referer': PLAN_BASE_URL,
|
||||
},
|
||||
cf: {
|
||||
cacheTtl: 30,
|
||||
cacheEverything: true,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@ -6,9 +6,10 @@
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: Oxanium, sans-serif;
|
||||
--font-sans: var(--font-chivo-mono), Chivo Mono, monospace;
|
||||
--font-mono: Fira Code, monospace;
|
||||
--font-serif: Merriweather, serif;
|
||||
--font-serif: var(--font-merriweather), Merriweather, serif;
|
||||
--font-heading: var(--font-merriweather), Merriweather, serif;
|
||||
--radius: 0.3rem;
|
||||
--tracking-normal: 0em;
|
||||
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
|
||||
@ -189,12 +190,144 @@
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
html {
|
||||
/* Cross-browser viewport height fix */
|
||||
height: 100%;
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-family: var(--font-chivo-mono), Chivo Mono, monospace;
|
||||
letter-spacing: var(--tracking-normal);
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
min-height: -webkit-fill-available;
|
||||
overflow-x: hidden;
|
||||
/* Prevent horizontal scrollbar */
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--font-merriweather), Merriweather, serif;
|
||||
}
|
||||
h1 {
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
h2 {
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
h3 {
|
||||
line-height: 1.3;
|
||||
}
|
||||
p, body {
|
||||
line-height: 1.6;
|
||||
}
|
||||
/* Ensure consistent box-sizing */
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide scrollbar but keep scroll functionality */
|
||||
.hide-scrollbar {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
}
|
||||
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera */
|
||||
}
|
||||
|
||||
/* Card depth utilities using theme shadow variables */
|
||||
.card-depth-1 {
|
||||
box-shadow: var(--shadow-lg), inset 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.card-depth-2 {
|
||||
box-shadow: var(--shadow-2xl), inset 0 2px 4px 0 rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* Text shadow utilities using foreground with opacity */
|
||||
.text-shadow-sm {
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.text-shadow-md {
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.text-shadow-lg {
|
||||
text-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Noise texture using SVG pattern */
|
||||
.noise-texture {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.noise-texture::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0.03;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Typography scale utilities */
|
||||
.text-heading-1 {
|
||||
font-size: clamp(2.5rem, 8vw, 6rem);
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.text-heading-2 {
|
||||
font-size: clamp(1.875rem, 4vw, 3rem);
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.text-heading-3 {
|
||||
font-size: clamp(1.25rem, 2.5vw, 1.875rem);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* Theme wipe overlay - shows old theme on left, reveals new theme on right */
|
||||
.theme-wipe-overlay {
|
||||
background: var(--background);
|
||||
backgroundSize: cover;
|
||||
backgroundPosition: center;
|
||||
backgroundRepeat: no-repeat;
|
||||
WebkitMaskImage: linear-gradient(to right, transparent 0%, transparent 0%, black 0%, black 100%);
|
||||
maskImage: linear-gradient(to right, transparent 0%, transparent 0%, black 0%, black 100%);
|
||||
WebkitMaskSize: 100% 100%;
|
||||
maskSize: 100% 100%;
|
||||
WebkitMaskRepeat: no-repeat;
|
||||
maskRepeat: no-repeat;
|
||||
transition: opacity 0.1s ease-out;
|
||||
}
|
||||
|
||||
.theme-wipe-overlay.theme-wipe-active {
|
||||
animation: theme-wipe 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
}
|
||||
|
||||
@keyframes theme-wipe {
|
||||
0% {
|
||||
WebkitMaskImage: linear-gradient(to right, transparent 0%, transparent 0%, black 0%, black 100%);
|
||||
maskImage: linear-gradient(to right, transparent 0%, transparent 0%, black 0%, black 100%);
|
||||
}
|
||||
100% {
|
||||
WebkitMaskImage: linear-gradient(to right, transparent 0%, transparent 100%, black 100%, black 100%);
|
||||
maskImage: linear-gradient(to right, transparent 0%, transparent 100%, black 100%, black 100%);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Rocker Switch Styles */
|
||||
.rocker {
|
||||
display: inline-block;
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import { Geist, Geist_Mono, Merriweather, Chivo_Mono } from "next/font/google";
|
||||
import { Toaster } from 'sonner';
|
||||
import { ThemeProvider } from "@/lib/theme-provider";
|
||||
import { Navigation } from "@/components/navigation";
|
||||
import { CustomFooter } from "@/components/custom-footer";
|
||||
import "./globals.css";
|
||||
|
||||
@ -16,6 +15,17 @@ const geistMono = Geist_Mono({
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const merriweather = Merriweather({
|
||||
variable: "--font-merriweather",
|
||||
subsets: ["latin"],
|
||||
weight: ["300", "400", "700", "900"],
|
||||
});
|
||||
|
||||
const chivoMono = Chivo_Mono({
|
||||
variable: "--font-chivo-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: "BiohazardVFX Minecraft Server | Join Our SMP Community",
|
||||
@ -76,7 +86,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
className={`${geistSans.variable} ${geistMono.variable} ${merriweather.variable} ${chivoMono.variable} antialiased`}
|
||||
>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
@ -84,7 +94,6 @@ export default function RootLayout({
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<Navigation />
|
||||
{children}
|
||||
<CustomFooter />
|
||||
<Toaster position="bottom-right" />
|
||||
|
||||
@ -9,6 +9,7 @@ import { Hero } from "@/components/hero";
|
||||
import { ServerStatus } from "@/components/server-status";
|
||||
import { Leaderboard } from "@/components/leaderboard";
|
||||
import { MinecraftServerStructuredData } from "@/components/structured-data";
|
||||
import { ThemeToggle } from "@/components/theme-toggle";
|
||||
import { MessageCircle } from "lucide-react";
|
||||
|
||||
interface ServerData {
|
||||
@ -65,43 +66,57 @@ export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<MinecraftServerStructuredData serverData={structuredDataProps} />
|
||||
<div className="relative min-h-svh overflow-hidden">
|
||||
<div className="relative min-h-screen min-h-svh overflow-hidden">
|
||||
<div className="absolute inset-0">
|
||||
{/* Light mode background */}
|
||||
<Image
|
||||
src="/backgroundimg.avif"
|
||||
alt="Minecraft background"
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="object-cover"
|
||||
className="object-cover dark:hidden"
|
||||
priority
|
||||
/>
|
||||
{/* Dark mode background */}
|
||||
<Image
|
||||
src="/backgroundimg-dark.avif"
|
||||
alt="Minecraft background dark"
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="hidden dark:block object-cover"
|
||||
priority
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-black/52 via-black/28 to-black/10 backdrop-blur-[1.5px]" />
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top_left,_rgba(255,255,255,0.32),_transparent_60%)]" />
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top_left,_rgba(255,255,255,0.32),_transparent_60%)] dark:bg-[radial-gradient(circle_at_top_left,_rgba(255,255,255,0.15),_transparent_60%)]" />
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex min-h-svh flex-col font-sans">
|
||||
<main className="flex flex-1 items-center justify-center px-6 py-16 sm:px-10 lg:px-14">
|
||||
<div className="w-full max-w-7xl">
|
||||
<div className="flex gap-6 items-start relative">
|
||||
<div className="relative z-10 flex min-h-screen min-h-svh flex-col font-sans overflow-x-hidden">
|
||||
<main className="flex flex-1 items-center justify-center px-6 py-16 sm:px-10 lg:px-14 overflow-x-hidden">
|
||||
<div className="w-full max-w-7xl mx-auto">
|
||||
<div className="flex gap-6 items-stretch relative">
|
||||
{/* Leaderboard - Left Side (separate card) */}
|
||||
<section className="hidden lg:flex lg:flex-shrink-0 w-[280px] rounded-3xl border border-white/10 bg-background/96 p-4 shadow-2xl backdrop-blur-xl self-start overflow-visible" style={{ maxHeight: '650px' }}>
|
||||
<section className="hidden lg:flex lg:flex-shrink-0 w-[300px] rounded-3xl border-2 border-white/10 bg-background/96 p-6 shadow-2xl backdrop-blur-xl backdrop-saturate-150 overflow-hidden card-depth-2 noise-texture" style={{ maxHeight: '650px' }}>
|
||||
<Leaderboard className="h-full" />
|
||||
</section>
|
||||
|
||||
{/* Main Card - Centered */}
|
||||
<div className="flex-1 flex justify-center">
|
||||
<section className="w-full max-w-5xl rounded-3xl border border-white/10 bg-background/96 p-8 shadow-2xl backdrop-blur-xl sm:p-10 lg:p-12" id="main-card">
|
||||
<div className="grid gap-10 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,0.95fr)]">
|
||||
<div className="flex-1 flex justify-center min-w-0">
|
||||
<section className="w-full max-w-6xl rounded-3xl border-2 border-white/10 bg-background/96 p-8 shadow-2xl backdrop-blur-xl backdrop-saturate-150 sm:p-10 lg:p-12 overflow-hidden relative card-depth-2 noise-texture" id="main-card">
|
||||
{/* Theme Toggle - Top Right */}
|
||||
<div className="absolute top-6 right-6 z-10">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
<div className="grid gap-8 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
|
||||
<Hero />
|
||||
|
||||
<div className="flex flex-col gap-6" id="server-status">
|
||||
<div className="rounded-3xl border border-border/40 bg-background p-6 shadow-lg">
|
||||
<div className="flex flex-col gap-1 sm:flex-row sm:items-end sm:justify-between text-foreground">
|
||||
<div className="flex flex-col gap-6 border-t border-white/10 pt-6 lg:border-t-0 lg:pt-0 lg:border-l lg:pl-8" id="server-status">
|
||||
<div className="rounded-3xl border border-border/40 bg-background p-6 shadow-lg backdrop-blur-sm card-depth-1 noise-texture">
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between text-foreground mb-6">
|
||||
<div>
|
||||
<p className="text-xs uppercase tracking-[0.35em] text-muted-foreground">
|
||||
Live snapshot
|
||||
</p>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
<h2 className="text-2xl font-semibold tracking-tight mt-1">
|
||||
Server Pulse
|
||||
</h2>
|
||||
</div>
|
||||
@ -109,13 +124,11 @@ export default function Home() {
|
||||
Auto refresh • 30s
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-6">
|
||||
<ServerStatus className="h-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
className="rounded-3xl border border-border/50 bg-background/50 p-6 shadow-xl backdrop-blur"
|
||||
className="rounded-3xl border border-border/50 bg-background/50 p-6 shadow-xl backdrop-blur backdrop-saturate-150 transition-all duration-200 ease-out hover:scale-[1.02] hover:border-primary/50 card-depth-1 noise-texture"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 1.2, ease: [0.33, 1, 0.68, 1] }}
|
||||
@ -149,7 +162,7 @@ export default function Home() {
|
||||
].map((item, index) => (
|
||||
<motion.div
|
||||
key={item.label}
|
||||
className="group rounded-xl border border-border/50 bg-background/95 p-4 shadow-md transition-all hover:border-primary/30 hover:shadow-lg"
|
||||
className="group rounded-xl border border-border/50 bg-background/95 p-4 shadow-md transition-all duration-200 ease-out hover:scale-[1.02] hover:border-primary/30 hover:shadow-lg"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 1.5 + index * 0.1, ease: [0.33, 1, 0.68, 1] }}
|
||||
@ -172,7 +185,7 @@ export default function Home() {
|
||||
>
|
||||
<Button
|
||||
size="lg"
|
||||
className="mt-6 w-full font-bold shadow-lg transition-all hover:shadow-xl"
|
||||
className="mt-6 w-full font-bold shadow-lg transition-all duration-200 ease-out hover:scale-[1.02] hover:shadow-xl hover:shadow-primary/20 active:scale-[0.98]"
|
||||
asChild
|
||||
>
|
||||
<a
|
||||
@ -194,7 +207,7 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
{/* Leaderboard Section - Mobile */}
|
||||
<section className="mt-10 lg:hidden rounded-3xl border border-white/10 bg-background/96 p-8 shadow-2xl backdrop-blur-xl sm:p-10 lg:p-12">
|
||||
<section className="mt-12 lg:hidden rounded-3xl border-2 border-white/10 bg-background/96 p-8 shadow-2xl backdrop-blur-xl backdrop-saturate-150 sm:p-10 lg:p-12 card-depth-2 noise-texture">
|
||||
<Leaderboard />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import StickyFooter from '@/components/ui/footer';
|
||||
import { motion } from 'framer-motion';
|
||||
import { MessageCircle } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
@ -42,7 +40,7 @@ export function CustomFooter() {
|
||||
try {
|
||||
await navigator.clipboard.writeText('minecraft.biohazardvfx.com');
|
||||
toast.success('Server IP copied to clipboard!');
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error('Failed to copy IP address');
|
||||
}
|
||||
};
|
||||
@ -66,9 +64,8 @@ export function CustomFooter() {
|
||||
},
|
||||
},
|
||||
}}
|
||||
className="bg-gradient-to-br from-card via-muted to-card/90 py-6 md:py-12 px-4 md:px-12 h-full w-full flex flex-col justify-between relative overflow-hidden"
|
||||
className="bg-background/96 backdrop-blur-xl border-t border-white/10 py-8 sm:py-10 lg:py-12 px-6 sm:px-10 lg:px-14 h-full w-full flex flex-col justify-between relative overflow-hidden"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-background/20 to-transparent pointer-events-none" />
|
||||
|
||||
{/* Navigation Section */}
|
||||
<motion.div className="relative z-10">
|
||||
@ -81,11 +78,11 @@ export function CustomFooter() {
|
||||
transition={{ delay: 0.3 + index * 0.1, duration: 0.6 }}
|
||||
className="flex flex-col gap-2"
|
||||
>
|
||||
<h3 className="mb-2 uppercase text-muted-foreground text-xs font-semibold tracking-wider border-b border-border pb-1 hover:text-foreground transition-colors duration-300">
|
||||
<h3 className="mb-2 uppercase text-muted-foreground text-xs font-semibold tracking-[0.35em] border-b border-white/10 pb-1 hover:text-foreground transition-colors duration-300">
|
||||
{section.title}
|
||||
</h3>
|
||||
{section.links.map((link, linkIndex) => {
|
||||
if (link.external) {
|
||||
if ('external' in link && link.external) {
|
||||
return (
|
||||
<motion.a
|
||||
key={linkIndex}
|
||||
@ -93,7 +90,7 @@ export function CustomFooter() {
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
whileHover={{ x: 8 }}
|
||||
className="text-muted-foreground hover:text-foreground transition-colors duration-300 font-sans text-xs md:text-sm group relative"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors duration-300 text-xs md:text-sm group relative"
|
||||
>
|
||||
<span className="relative">
|
||||
{link.text}
|
||||
@ -106,13 +103,13 @@ export function CustomFooter() {
|
||||
</span>
|
||||
</motion.a>
|
||||
);
|
||||
} else if (link.isButton) {
|
||||
} else if ('isButton' in link && link.isButton) {
|
||||
return (
|
||||
<motion.button
|
||||
key={linkIndex}
|
||||
onClick={handleCopyIP}
|
||||
whileHover={{ x: 8 }}
|
||||
className="text-muted-foreground hover:text-foreground transition-colors duration-300 font-sans text-xs md:text-sm text-left group relative"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors duration-300 text-xs md:text-sm text-left group relative"
|
||||
>
|
||||
<span className="relative">
|
||||
{link.text}
|
||||
@ -125,11 +122,11 @@ export function CustomFooter() {
|
||||
</span>
|
||||
</motion.button>
|
||||
);
|
||||
} else if (link.isStatic) {
|
||||
} else if ('isStatic' in link && link.isStatic) {
|
||||
return (
|
||||
<span
|
||||
key={linkIndex}
|
||||
className="text-muted-foreground font-sans text-xs md:text-sm"
|
||||
className="text-muted-foreground text-xs md:text-sm"
|
||||
>
|
||||
{link.text}
|
||||
</span>
|
||||
@ -140,7 +137,7 @@ export function CustomFooter() {
|
||||
key={linkIndex}
|
||||
href={link.href}
|
||||
whileHover={{ x: 8 }}
|
||||
className="text-muted-foreground hover:text-foreground transition-colors duration-300 font-sans text-xs md:text-sm group relative"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors duration-300 text-xs md:text-sm group relative"
|
||||
>
|
||||
<span className="relative">
|
||||
{link.text}
|
||||
@ -173,7 +170,7 @@ export function CustomFooter() {
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 1, duration: 0.8 }}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
className="text-[12vw] md:text-[10vw] lg:text-[8vw] xl:text-[6vw] leading-[0.8] font-serif bg-gradient-to-r from-foreground via-muted-foreground to-foreground/60 bg-clip-text text-transparent cursor-default"
|
||||
className="text-[12vw] md:text-[10vw] lg:text-[8vw] xl:text-[6vw] leading-[0.8] text-foreground cursor-default"
|
||||
>
|
||||
{footerData.title}
|
||||
</motion.h1>
|
||||
@ -192,7 +189,7 @@ export function CustomFooter() {
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 1.4, duration: 0.5 }}
|
||||
className="text-muted-foreground text-xs md:text-sm font-sans hover:text-foreground transition-colors duration-300"
|
||||
className="text-muted-foreground text-xs md:text-sm hover:text-foreground transition-colors duration-300"
|
||||
>
|
||||
{footerData.subtitle}
|
||||
</motion.p>
|
||||
@ -219,10 +216,10 @@ export function CustomFooter() {
|
||||
rel="noopener noreferrer"
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
className="inline-flex items-center gap-2 w-8 h-8 md:w-10 md:h-10 rounded-full bg-muted hover:bg-gradient-to-r hover:from-primary hover:to-secondary transition-colors duration-300 justify-center"
|
||||
className="inline-flex items-center gap-2 w-8 h-8 md:w-10 md:h-10 rounded-full border border-white/10 bg-background/50 hover:bg-primary hover:border-primary/50 transition-all duration-300 justify-center backdrop-blur-sm"
|
||||
aria-label="Join Discord"
|
||||
>
|
||||
<MessageCircle className="h-4 w-4 md:h-5 md:w-5 text-muted-foreground group-hover:text-primary-foreground" />
|
||||
<MessageCircle className="h-4 w-4 md:h-5 md:w-5 text-muted-foreground group-hover:text-primary-foreground transition-colors duration-300" />
|
||||
</motion.a>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
@ -15,6 +15,7 @@ const screenshots = [
|
||||
'/2025-07-13_12.59.13.png',
|
||||
'/2025-07-13_13.01.28.png',
|
||||
'/2025-10-22_17.07.42.png',
|
||||
'/backgroundimg.avif',
|
||||
];
|
||||
|
||||
export function Hero() {
|
||||
@ -52,9 +53,9 @@ export function Hero() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-xl space-y-8">
|
||||
<div className="w-full max-w-xl space-y-6 min-w-0">
|
||||
<motion.div
|
||||
className="flex flex-col gap-6 text-foreground"
|
||||
className="flex flex-col gap-6 text-foreground min-w-0"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
@ -68,24 +69,24 @@ export function Hero() {
|
||||
Biohazard SMP
|
||||
</motion.span>
|
||||
<motion.h1
|
||||
className="text-6xl font-bold leading-[1.1] tracking-tight sm:text-7xl lg:text-8xl"
|
||||
className="text-6xl font-bold leading-[1.1] break-words overflow-wrap-anywhere sm:text-7xl lg:text-8xl"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
>
|
||||
<span className="block text-primary">
|
||||
<span className="block text-primary break-words tracking-tighter text-shadow-md">
|
||||
<HoverRollingText
|
||||
text="Build."
|
||||
transition={{ duration: 0.6, delay: 0.05 }}
|
||||
/>
|
||||
</span>
|
||||
<span className="block text-secondary">
|
||||
<span className="block text-secondary break-words tracking-normal text-shadow-sm">
|
||||
<HoverRollingText
|
||||
text="Explore."
|
||||
transition={{ duration: 0.6, delay: 0.05 }}
|
||||
/>
|
||||
</span>
|
||||
<span className="block text-accent">
|
||||
<span className="block text-accent break-words tracking-normal text-shadow-sm">
|
||||
<HoverRollingText
|
||||
text="Survive."
|
||||
transition={{ duration: 0.6, delay: 0.05 }}
|
||||
@ -93,7 +94,7 @@ export function Hero() {
|
||||
</span>
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="max-w-md text-base leading-relaxed text-muted-foreground sm:text-lg"
|
||||
className="max-w-md text-base leading-relaxed text-muted-foreground sm:text-lg break-words"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.5 }}
|
||||
@ -109,19 +110,19 @@ export function Hero() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.7 }}
|
||||
>
|
||||
<div className="group rounded-2xl border border-border/50 bg-card/50 p-6 shadow-xl backdrop-blur transition-all hover:border-primary/30 hover:shadow-2xl">
|
||||
<div className="group rounded-2xl border border-border/50 bg-card/50 p-6 shadow-xl backdrop-blur backdrop-saturate-150 transition-all duration-200 ease-out hover:scale-[1.02] hover:border-primary/30 hover:shadow-2xl noise-texture">
|
||||
<span className="text-xs font-bold uppercase tracking-[0.2em] text-muted-foreground">
|
||||
Server IP
|
||||
</span>
|
||||
<div className="mt-4 flex items-center justify-between gap-4 rounded-xl border border-border/50 bg-background/90 px-5 py-4 shadow-sm">
|
||||
<code className="text-base font-mono font-bold text-foreground sm:text-lg">
|
||||
<div className="mt-4 flex items-center justify-between gap-4 rounded-xl border border-border/50 bg-background/90 px-5 py-4 shadow-sm min-w-0">
|
||||
<code className="text-base font-mono font-bold text-foreground sm:text-lg break-all min-w-0">
|
||||
{serverAddress}
|
||||
</code>
|
||||
<Button
|
||||
onClick={copyServerAddress}
|
||||
size="icon-sm"
|
||||
variant="outline"
|
||||
className="shrink-0 border-primary/30 bg-primary/10 hover:bg-primary/20 hover:border-primary/50 transition-all"
|
||||
className="shrink-0 border-primary/30 bg-primary/10 transition-all duration-200 ease-out hover:bg-primary/20 hover:border-primary/50 hover:shadow-lg hover:shadow-primary/20 active:scale-[0.98]"
|
||||
aria-label={copied ? 'Server address copied' : 'Copy server address'}
|
||||
>
|
||||
{copied ? <Check className="h-4 w-4 text-primary" /> : <Copy className="h-4 w-4 text-primary" />}
|
||||
@ -132,7 +133,7 @@ export function Hero() {
|
||||
|
||||
{/* Screenshot Showcase */}
|
||||
<motion.div
|
||||
className="group relative aspect-video overflow-hidden rounded-3xl border border-border/50 bg-card/50 shadow-2xl backdrop-blur transition-all hover:shadow-3xl cursor-pointer"
|
||||
className="group relative aspect-video overflow-hidden rounded-3xl border-2 border-white/10 bg-card/50 shadow-2xl backdrop-blur-xl backdrop-saturate-150 transition-all duration-200 ease-out hover:scale-[1.02] hover:border-primary/50 hover:shadow-2xl hover:shadow-primary/20 cursor-pointer noise-texture"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.9, ease: [0.33, 1, 0.68, 1] }}
|
||||
|
||||
@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
|
||||
import { motion } from 'motion/react';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Trophy, Clock, Calendar } from 'lucide-react';
|
||||
import { Trophy, Clock } from 'lucide-react';
|
||||
import { PlayerTooltip } from '@/components/player-tooltip';
|
||||
|
||||
interface LeaderboardPlayer {
|
||||
@ -64,8 +64,8 @@ export function Leaderboard({ className }: { className?: string }) {
|
||||
<Trophy className="h-4 w-4 text-primary" />
|
||||
<h2 className="text-base font-semibold">Top Players</h2>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto" style={{ minHeight: 0 }}>
|
||||
<div className="space-y-2 pr-2">
|
||||
<div className="flex-1 overflow-y-auto hide-scrollbar" style={{ minHeight: 0 }}>
|
||||
<div className="space-y-2">
|
||||
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||
<div key={i} className="flex items-center gap-2 p-2 rounded-lg border border-border/50">
|
||||
<Skeleton className="h-5 w-5 rounded-full" />
|
||||
@ -109,8 +109,8 @@ export function Leaderboard({ className }: { className?: string }) {
|
||||
<Trophy className="h-4 w-4 text-primary" />
|
||||
<h2 className="text-base font-semibold">Top Players</h2>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto" style={{ minHeight: 0 }}>
|
||||
<div className="space-y-1.5 pr-2" style={{ paddingBottom: '2.5rem' }}>
|
||||
<div className="flex-1 overflow-y-auto hide-scrollbar" style={{ minHeight: 0 }}>
|
||||
<div className="space-y-1.5" style={{ paddingBottom: '2.5rem' }}>
|
||||
{data.players.slice(0, 10).map((player, index) => {
|
||||
const rank = index + 1;
|
||||
const isTopThree = rank <= 3;
|
||||
@ -119,21 +119,21 @@ export function Leaderboard({ className }: { className?: string }) {
|
||||
<motion.div
|
||||
key={player.name}
|
||||
className={cn(
|
||||
"flex items-center gap-2 p-2 rounded-lg border transition-all text-sm",
|
||||
"flex items-center gap-2 p-2 rounded-lg border transition-all duration-200 ease-out text-sm",
|
||||
isTopThree
|
||||
? "bg-primary/5 border-primary/20 shadow-sm"
|
||||
: "bg-background/50 border-border/50 hover:border-primary/30"
|
||||
? "bg-primary/5 border-primary/20 shadow-sm hover:scale-[1.02] hover:border-primary/30"
|
||||
: "bg-background/50 border-border/50 hover:scale-[1.02] hover:border-primary/30"
|
||||
)}
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.2, delay: index * 0.03 }}
|
||||
transition={{ duration: 0.2, delay: index * 0.03, ease: [0.4, 0, 0.2, 1] }}
|
||||
>
|
||||
<div className={cn(
|
||||
"flex items-center justify-center w-6 h-6 rounded-full font-bold text-xs flex-shrink-0",
|
||||
rank === 1 && "bg-primary text-primary-foreground",
|
||||
rank === 2 && "bg-secondary text-secondary-foreground",
|
||||
rank === 3 && "bg-accent text-accent-foreground",
|
||||
rank > 3 && "bg-muted text-muted-foreground"
|
||||
"flex items-center justify-center w-6 h-6 rounded-full font-bold text-xs flex-shrink-0 border transition-all duration-200",
|
||||
rank === 1 && "bg-primary/10 border-primary/30 text-primary",
|
||||
rank === 2 && "bg-primary/10 border-primary/30 text-primary",
|
||||
rank === 3 && "bg-primary/10 border-primary/30 text-primary",
|
||||
rank > 3 && "bg-muted text-muted-foreground border-border/50"
|
||||
)}>
|
||||
{rank <= 3 ? getRankBadge(rank) : rank}
|
||||
</div>
|
||||
|
||||
@ -1,45 +1,151 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Menu, MenuItem, HoveredLink } from '@/components/ui/navbar-menu';
|
||||
import {
|
||||
NavigationMenu,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuList,
|
||||
navigationMenuTriggerStyle,
|
||||
} from '@/components/ui/navigation-menu';
|
||||
import { ThemeToggle } from '@/components/theme-toggle';
|
||||
import { MessageCircle } from 'lucide-react';
|
||||
import { MessageCircle, Menu as MenuIcon, X } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
interface NavLink {
|
||||
label: string;
|
||||
href: string;
|
||||
external?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
const navItems: NavLink[] = [
|
||||
{
|
||||
label: 'Home',
|
||||
href: '/',
|
||||
},
|
||||
{
|
||||
label: 'Server',
|
||||
href: '/#server-status',
|
||||
},
|
||||
{
|
||||
label: 'Community',
|
||||
href: 'https://discord.gg/58FnVzmzrS',
|
||||
external: true,
|
||||
icon: <MessageCircle className="h-4 w-4" />,
|
||||
},
|
||||
];
|
||||
|
||||
export function Navigation() {
|
||||
const [active, setActive] = useState<string | null>(null);
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const pathname = usePathname();
|
||||
|
||||
// Close mobile menu on route change
|
||||
useEffect(() => {
|
||||
setIsMenuOpen(false);
|
||||
}, [pathname]);
|
||||
|
||||
const isActive = (href: string) => {
|
||||
if (href.startsWith('/#')) {
|
||||
return pathname === '/';
|
||||
}
|
||||
return pathname === href;
|
||||
};
|
||||
|
||||
const handleNavClick = (e: React.MouseEvent, href: string) => {
|
||||
if (href.startsWith('/#')) {
|
||||
e.preventDefault();
|
||||
const id = href.replace('/#', '');
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed top-4 left-1/2 -translate-x-1/2 z-50 w-full max-w-6xl px-4">
|
||||
<Menu setActive={setActive}>
|
||||
<MenuItem setActive={setActive} active={active} item="Home">
|
||||
<div className="flex flex-col space-y-4 text-sm">
|
||||
<HoveredLink href="/">Home</HoveredLink>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem setActive={setActive} active={active} item="Server">
|
||||
<div className="flex flex-col space-y-4 text-sm">
|
||||
<HoveredLink href="/#server-status">Server Status</HoveredLink>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem setActive={setActive} active={active} item="Community">
|
||||
<div className="flex flex-col space-y-4 text-sm">
|
||||
<HoveredLink
|
||||
href="https://discord.gg/58FnVzmzrS"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2"
|
||||
<>
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden md:flex fixed top-4 left-1/2 -translate-x-1/2 z-50 justify-center items-center overflow-x-hidden">
|
||||
<NavigationMenu className="px-2 py-2 overflow-hidden">
|
||||
<NavigationMenuList className="gap-2 flex-wrap">
|
||||
{navItems.map((item) => (
|
||||
<NavigationMenuItem key={item.label}>
|
||||
<NavigationMenuLink asChild>
|
||||
<Link
|
||||
href={item.href}
|
||||
target={item.external ? '_blank' : undefined}
|
||||
rel={item.external ? 'noopener noreferrer' : undefined}
|
||||
onClick={(e) => handleNavClick(e, item.href)}
|
||||
className={cn(
|
||||
navigationMenuTriggerStyle(),
|
||||
'text-foreground hover:text-primary hover:bg-accent focus:bg-accent focus:text-primary whitespace-nowrap',
|
||||
isActive(item.href) && 'bg-accent/50 text-accent-foreground'
|
||||
)}
|
||||
>
|
||||
<MessageCircle className="h-4 w-4" />
|
||||
Discord
|
||||
</HoveredLink>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<div className="flex items-center justify-center">
|
||||
<span className="flex items-center gap-2">
|
||||
{item.icon}
|
||||
{item.label}
|
||||
</span>
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
))}
|
||||
|
||||
{/* Theme Toggle */}
|
||||
<NavigationMenuItem>
|
||||
<div className="px-2 flex-shrink-0">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</Menu>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
</nav>
|
||||
|
||||
{/* Mobile Navigation */}
|
||||
<div className="md:hidden fixed top-4 right-4 z-50">
|
||||
<button
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
className="rounded-full border border-border/50 bg-background/95 backdrop-blur-md shadow-lg p-3"
|
||||
aria-label="Toggle navigation menu"
|
||||
aria-expanded={isMenuOpen}
|
||||
>
|
||||
{isMenuOpen ? (
|
||||
<X className="h-5 w-5 text-foreground" />
|
||||
) : (
|
||||
<MenuIcon className="h-5 w-5 text-foreground" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Mobile Menu Panel */}
|
||||
{isMenuOpen && (
|
||||
<div className="absolute right-0 mt-3 w-64 max-w-[calc(100vw-2rem)] rounded-2xl border border-border/50 bg-background/95 backdrop-blur-md shadow-lg p-4 overflow-hidden">
|
||||
<nav className="flex flex-col space-y-2">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.label}
|
||||
href={item.href}
|
||||
target={item.external ? '_blank' : undefined}
|
||||
rel={item.external ? 'noopener noreferrer' : undefined}
|
||||
onClick={(e) => handleNavClick(e, item.href)}
|
||||
className={cn(
|
||||
'flex items-center gap-2 px-4 py-3 rounded-lg text-foreground hover:text-primary hover:bg-accent transition-colors break-words',
|
||||
isActive(item.href) && 'bg-accent/50 text-accent-foreground'
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
<div className="px-4 py-3">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ export function ServerStatus({ className }: ServerStatusProps = {}) {
|
||||
return (
|
||||
<motion.div
|
||||
className={cn(
|
||||
"space-y-6 rounded-2xl border border-border/50 bg-card/50 p-6 text-card-foreground shadow-xl backdrop-blur",
|
||||
"space-y-5 text-card-foreground",
|
||||
className
|
||||
)}
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
@ -239,15 +239,10 @@ export function ServerStatus({ className }: ServerStatusProps = {}) {
|
||||
>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<motion.span
|
||||
className="h-3 w-3 rounded-full bg-emerald-500 shadow-lg"
|
||||
className="h-3 w-3 rounded-full bg-primary shadow-lg"
|
||||
animate={{
|
||||
scale: [1, 1.2, 1],
|
||||
opacity: [1, 0.8, 1],
|
||||
boxShadow: [
|
||||
'0 0 0px rgba(16, 185, 129, 0.4)',
|
||||
'0 0 10px rgba(16, 185, 129, 0.8)',
|
||||
'0 0 0px rgba(16, 185, 129, 0.4)',
|
||||
],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2,
|
||||
@ -256,7 +251,7 @@ export function ServerStatus({ className }: ServerStatusProps = {}) {
|
||||
}}
|
||||
/>
|
||||
<motion.span
|
||||
className="text-sm font-bold text-emerald-600 dark:text-emerald-400"
|
||||
className="text-sm font-bold text-primary"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.6 }}
|
||||
@ -270,7 +265,7 @@ export function ServerStatus({ className }: ServerStatusProps = {}) {
|
||||
</motion.div>
|
||||
|
||||
<motion.dl
|
||||
className="grid min-w-0 gap-3 sm:grid-cols-2"
|
||||
className="grid min-w-0 gap-4 sm:grid-cols-2"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.4, delay: 0.6, ease: [0.33, 1, 0.68, 1] }}
|
||||
@ -279,20 +274,25 @@ export function ServerStatus({ className }: ServerStatusProps = {}) {
|
||||
{statTiles.map((tile, index) => (
|
||||
<motion.div
|
||||
key={tile.label}
|
||||
className="group min-w-0 overflow-hidden rounded-xl border border-border/50 bg-background/95 p-4 shadow-md transition-all hover:border-primary/30 hover:shadow-lg"
|
||||
className="group min-w-0 overflow-hidden rounded-xl border border-border/50 bg-background/95 p-5 shadow-md backdrop-blur-sm transition-all duration-200 ease-out hover:scale-[1.02] hover:border-primary/30 hover:shadow-lg noise-texture"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.7 + index * 0.1, ease: [0.33, 1, 0.68, 1] }}
|
||||
style={{ backfaceVisibility: "hidden", WebkitBackfaceVisibility: "hidden", transform: "translateZ(0)" }}
|
||||
>
|
||||
<dt className="truncate text-xs font-semibold uppercase tracking-[0.08em] text-muted-foreground">
|
||||
<dt className="text-xs font-semibold uppercase tracking-[0.08em] text-muted-foreground break-words">
|
||||
{tile.label}
|
||||
</dt>
|
||||
<dd className="mt-2 truncate text-2xl font-bold text-foreground">
|
||||
<dd className={cn(
|
||||
"mt-3 text-2xl font-bold break-words break-all",
|
||||
(tile.label === 'Players Online' || tile.label === 'Total Players')
|
||||
? "text-primary"
|
||||
: "text-foreground"
|
||||
)}>
|
||||
{tile.value}
|
||||
</dd>
|
||||
{tile.hint ? (
|
||||
<p className="mt-1 truncate text-[11px] font-medium uppercase tracking-[0.05em] text-muted-foreground/70">
|
||||
<p className="mt-2 text-[11px] font-medium uppercase tracking-[0.05em] text-muted-foreground/70 break-words">
|
||||
{tile.hint}
|
||||
</p>
|
||||
) : null}
|
||||
@ -319,9 +319,9 @@ export function ServerStatus({ className }: ServerStatusProps = {}) {
|
||||
)}
|
||||
|
||||
{status.stats?.averageTps ? (
|
||||
<div className="flex items-center justify-between rounded-xl border border-border/50 bg-background/95 px-4 py-3.5 shadow-md">
|
||||
<div className="flex items-center justify-between rounded-xl border border-border/50 bg-background/95 px-5 py-4 shadow-md backdrop-blur-sm transition-all duration-200 ease-out hover:scale-[1.02] hover:border-primary/30 hover:shadow-lg noise-texture">
|
||||
<span className="text-xs font-semibold uppercase tracking-[0.08em] text-muted-foreground">Average TPS (7d)</span>
|
||||
<span className="text-xl font-bold text-foreground">
|
||||
<span className="text-2xl font-bold text-foreground">
|
||||
{status.stats.averageTps}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { Moon, Sun } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export function ThemeToggle({ className }: { className?: string }) {
|
||||
@ -14,25 +14,32 @@ export function ThemeToggle({ className }: { className?: string }) {
|
||||
}, []);
|
||||
|
||||
if (!mounted) {
|
||||
return <div className={cn('h-9 w-16', className)} />;
|
||||
return <div className={cn('h-16 w-16', className)} />;
|
||||
}
|
||||
|
||||
const isDark = theme === 'dark';
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme(isDark ? 'light' : 'dark');
|
||||
};
|
||||
|
||||
return (
|
||||
<label className={cn("rocker rocker-small", className)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isDark}
|
||||
onChange={(e) => setTheme(e.target.checked ? 'dark' : 'light')}
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className={cn(
|
||||
"relative w-16 h-16 transition-transform hover:scale-110 active:scale-95 cursor-pointer",
|
||||
className
|
||||
)}
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
<Image
|
||||
src={isDark ? "/lever-down.png" : "/lever-up.png"}
|
||||
alt={isDark ? "Dark mode lever" : "Light mode lever"}
|
||||
width={64}
|
||||
height={64}
|
||||
className="object-contain"
|
||||
priority
|
||||
/>
|
||||
<span className="switch-left">
|
||||
<Sun className="h-3 w-3" />
|
||||
</span>
|
||||
<span className="switch-right">
|
||||
<Moon className="h-3 w-3" />
|
||||
</span>
|
||||
</label>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
61
src/components/theme-transition.tsx
Normal file
61
src/components/theme-transition.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { useTheme } from 'next-themes';
|
||||
|
||||
export function ThemeTransition() {
|
||||
const { theme, resolvedTheme } = useTheme();
|
||||
const [prevTheme, setPrevTheme] = useState<string | undefined>();
|
||||
const [capturedImage, setCapturedImage] = useState<string | null>(null);
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Listen for theme transition start event with captured image
|
||||
const handleTransitionStart = (event: CustomEvent<{ imageDataUrl: string; newTheme: string }>) => {
|
||||
setCapturedImage(event.detail.imageDataUrl);
|
||||
};
|
||||
|
||||
window.addEventListener('theme-transition-start', handleTransitionStart as EventListener);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('theme-transition-start', handleTransitionStart as EventListener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const currentTheme = resolvedTheme || theme;
|
||||
if (prevTheme && prevTheme !== currentTheme && currentTheme && overlayRef.current && capturedImage) {
|
||||
// Set the captured image as background
|
||||
overlayRef.current.style.backgroundImage = `url(${capturedImage})`;
|
||||
overlayRef.current.style.backgroundSize = 'cover';
|
||||
overlayRef.current.style.backgroundPosition = 'center';
|
||||
overlayRef.current.style.opacity = '1';
|
||||
overlayRef.current.classList.add('theme-wipe-active');
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
if (overlayRef.current) {
|
||||
overlayRef.current.classList.remove('theme-wipe-active');
|
||||
overlayRef.current.style.opacity = '0';
|
||||
overlayRef.current.style.backgroundImage = '';
|
||||
setCapturedImage(null);
|
||||
}
|
||||
}, 600);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}
|
||||
setPrevTheme(currentTheme);
|
||||
}, [theme, resolvedTheme, prevTheme, capturedImage]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={overlayRef}
|
||||
className="fixed inset-0 z-[9999] pointer-events-none theme-wipe-overlay"
|
||||
style={{
|
||||
opacity: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,11 +5,11 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 ease-out disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90 hover:shadow-lg hover:shadow-primary/20 active:scale-[0.98]",
|
||||
destructive:
|
||||
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
|
||||
@ -9,7 +9,6 @@ const containerVariants = {
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.8,
|
||||
ease: "easeOut",
|
||||
staggerChildren: 0.1,
|
||||
},
|
||||
},
|
||||
@ -20,7 +19,7 @@ const itemVariants = {
|
||||
visible: {
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
transition: { duration: 0.6, ease: "easeOut" },
|
||||
transition: { duration: 0.6 },
|
||||
},
|
||||
}
|
||||
|
||||
@ -29,7 +28,7 @@ const linkVariants = {
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.4, ease: "easeOut" },
|
||||
transition: { duration: 0.4 },
|
||||
},
|
||||
}
|
||||
|
||||
@ -39,7 +38,7 @@ const socialVariants = {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: {
|
||||
type: "spring",
|
||||
type: "spring" as const,
|
||||
stiffness: 200,
|
||||
damping: 10,
|
||||
},
|
||||
@ -53,7 +52,6 @@ const backgroundVariants = {
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 2,
|
||||
ease: "easeOut",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
const transition = {
|
||||
type: "spring",
|
||||
type: "spring" as const,
|
||||
mass: 0.5,
|
||||
damping: 11.5,
|
||||
stiffness: 100,
|
||||
@ -109,7 +109,7 @@ export const ProductItem = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const HoveredLink = ({ children, ...rest }: any) => {
|
||||
export const HoveredLink = ({ children, ...rest }: React.ComponentProps<typeof Link>) => {
|
||||
return (
|
||||
<Link
|
||||
{...rest}
|
||||
|
||||
168
src/components/ui/navigation-menu.tsx
Normal file
168
src/components/ui/navigation-menu.tsx
Normal file
@ -0,0 +1,168 @@
|
||||
import * as React from "react"
|
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
||||
import { cva } from "class-variance-authority"
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function NavigationMenu({
|
||||
className,
|
||||
children,
|
||||
viewport = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
|
||||
viewport?: boolean
|
||||
}) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Root
|
||||
data-slot="navigation-menu"
|
||||
data-viewport={viewport}
|
||||
className={cn(
|
||||
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{viewport && <NavigationMenuViewport />}
|
||||
</NavigationMenuPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuList({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.List
|
||||
data-slot="navigation-menu-list"
|
||||
className={cn(
|
||||
"group flex flex-1 list-none items-center justify-center gap-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Item
|
||||
data-slot="navigation-menu-item"
|
||||
className={cn("relative", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
|
||||
)
|
||||
|
||||
function NavigationMenuTrigger({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Trigger
|
||||
data-slot="navigation-menu-trigger"
|
||||
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}{" "}
|
||||
<ChevronDownIcon
|
||||
className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Content
|
||||
data-slot="navigation-menu-content"
|
||||
className={cn(
|
||||
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
|
||||
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuViewport({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute top-full left-0 isolate z-50 flex justify-center"
|
||||
)}
|
||||
>
|
||||
<NavigationMenuPrimitive.Viewport
|
||||
data-slot="navigation-menu-viewport"
|
||||
className={cn(
|
||||
"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuLink({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Link
|
||||
data-slot="navigation-menu-link"
|
||||
className={cn(
|
||||
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuIndicator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
data-slot="navigation-menu-indicator"
|
||||
className={cn(
|
||||
"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
|
||||
</NavigationMenuPrimitive.Indicator>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
NavigationMenu,
|
||||
NavigationMenuList,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuViewport,
|
||||
navigationMenuTriggerStyle,
|
||||
}
|
||||
@ -35,20 +35,21 @@ export const AnimatedTooltip = ({ items }: AnimatedTooltipProps) => {
|
||||
springConfig,
|
||||
);
|
||||
|
||||
const handleMouseMove = (event: any) => {
|
||||
const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (animationFrameRef.current) {
|
||||
cancelAnimationFrame(animationFrameRef.current);
|
||||
}
|
||||
|
||||
animationFrameRef.current = requestAnimationFrame(() => {
|
||||
const halfWidth = event.target.offsetWidth / 2;
|
||||
const target = event.target as HTMLElement;
|
||||
const halfWidth = target.offsetWidth / 2;
|
||||
x.set(event.nativeEvent.offsetX - halfWidth);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{items.map((item, idx) => (
|
||||
{items.map((item) => (
|
||||
<div
|
||||
className="group relative -mr-4"
|
||||
key={item.name}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useState } from "react";
|
||||
|
||||
export const Component = () => {
|
||||
return (
|
||||
|
||||
31
src/config/navigation.ts
Normal file
31
src/config/navigation.ts
Normal file
@ -0,0 +1,31 @@
|
||||
export interface NavItem {
|
||||
label: string;
|
||||
href: string;
|
||||
external?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface NavDropdown {
|
||||
label: string;
|
||||
items: Array<{
|
||||
title: string;
|
||||
description?: string;
|
||||
href: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export const navigationItems: Array<NavItem | NavDropdown> = [
|
||||
{
|
||||
label: "Home",
|
||||
href: "/",
|
||||
},
|
||||
{
|
||||
label: "Server",
|
||||
href: "/#server-status",
|
||||
},
|
||||
{
|
||||
label: "Community",
|
||||
href: "https://discord.gg/invite",
|
||||
external: true,
|
||||
},
|
||||
];
|
||||
@ -21,6 +21,8 @@
|
||||
"zone_name": "biohazardvfx.com"
|
||||
}
|
||||
],
|
||||
"workers_dev": true,
|
||||
"preview_urls": true,
|
||||
"observability": {
|
||||
"enabled": true
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user