-
Current soldiers: 0
-
+
+
+ Soldiers: 0
+
+
diff --git a/painter-js/package-lock.json b/painter-js/package-lock.json
deleted file mode 100644
index 702b221..0000000
--- a/painter-js/package-lock.json
+++ /dev/null
@@ -1,917 +0,0 @@
-{
- "name": "painter-js",
- "version": "0.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "painter-js",
- "version": "0.0.0",
- "dependencies": {
- "socket.io-client": "^4.7.5"
- },
- "devDependencies": {
- "typescript": "^5.2.2",
- "vite": "^5.2.0"
- }
- },
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
- "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
- "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
- "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
- "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
- "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
- "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
- "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
- "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
- "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
- "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
- "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
- "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
- "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
- "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
- "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
- "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
- "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
- "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
- "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
- "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
- "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
- "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
- "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz",
- "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz",
- "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz",
- "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz",
- "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz",
- "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz",
- "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz",
- "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz",
- "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz",
- "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz",
- "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz",
- "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz",
- "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz",
- "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz",
- "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz",
- "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz",
- "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@socket.io/component-emitter": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
- "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
- },
- "node_modules/@types/estree": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
- "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
- "dev": true
- },
- "node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/engine.io-client": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
- "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
- "dependencies": {
- "@socket.io/component-emitter": "~3.1.0",
- "debug": "~4.3.1",
- "engine.io-parser": "~5.2.1",
- "ws": "~8.11.0",
- "xmlhttprequest-ssl": "~2.0.0"
- }
- },
- "node_modules/engine.io-parser": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
- "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/esbuild": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
- "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
- "dev": true,
- "hasInstallScript": true,
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=12"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.20.2",
- "@esbuild/android-arm": "0.20.2",
- "@esbuild/android-arm64": "0.20.2",
- "@esbuild/android-x64": "0.20.2",
- "@esbuild/darwin-arm64": "0.20.2",
- "@esbuild/darwin-x64": "0.20.2",
- "@esbuild/freebsd-arm64": "0.20.2",
- "@esbuild/freebsd-x64": "0.20.2",
- "@esbuild/linux-arm": "0.20.2",
- "@esbuild/linux-arm64": "0.20.2",
- "@esbuild/linux-ia32": "0.20.2",
- "@esbuild/linux-loong64": "0.20.2",
- "@esbuild/linux-mips64el": "0.20.2",
- "@esbuild/linux-ppc64": "0.20.2",
- "@esbuild/linux-riscv64": "0.20.2",
- "@esbuild/linux-s390x": "0.20.2",
- "@esbuild/linux-x64": "0.20.2",
- "@esbuild/netbsd-x64": "0.20.2",
- "@esbuild/openbsd-x64": "0.20.2",
- "@esbuild/sunos-x64": "0.20.2",
- "@esbuild/win32-arm64": "0.20.2",
- "@esbuild/win32-ia32": "0.20.2",
- "@esbuild/win32-x64": "0.20.2"
- }
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "node_modules/nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
- },
- "node_modules/postcss": {
- "version": "8.4.38",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
- "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.2.0"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/rollup": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
- "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
- "dev": true,
- "dependencies": {
- "@types/estree": "1.0.5"
- },
- "bin": {
- "rollup": "dist/bin/rollup"
- },
- "engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
- },
- "optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.17.2",
- "@rollup/rollup-android-arm64": "4.17.2",
- "@rollup/rollup-darwin-arm64": "4.17.2",
- "@rollup/rollup-darwin-x64": "4.17.2",
- "@rollup/rollup-linux-arm-gnueabihf": "4.17.2",
- "@rollup/rollup-linux-arm-musleabihf": "4.17.2",
- "@rollup/rollup-linux-arm64-gnu": "4.17.2",
- "@rollup/rollup-linux-arm64-musl": "4.17.2",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2",
- "@rollup/rollup-linux-riscv64-gnu": "4.17.2",
- "@rollup/rollup-linux-s390x-gnu": "4.17.2",
- "@rollup/rollup-linux-x64-gnu": "4.17.2",
- "@rollup/rollup-linux-x64-musl": "4.17.2",
- "@rollup/rollup-win32-arm64-msvc": "4.17.2",
- "@rollup/rollup-win32-ia32-msvc": "4.17.2",
- "@rollup/rollup-win32-x64-msvc": "4.17.2",
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/socket.io-client": {
- "version": "4.7.5",
- "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz",
- "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==",
- "dependencies": {
- "@socket.io/component-emitter": "~3.1.0",
- "debug": "~4.3.2",
- "engine.io-client": "~6.5.2",
- "socket.io-parser": "~4.2.4"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/socket.io-parser": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
- "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
- "dependencies": {
- "@socket.io/component-emitter": "~3.1.0",
- "debug": "~4.3.1"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/source-map-js": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
- "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/typescript": {
- "version": "5.4.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
- "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
- "dev": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/vite": {
- "version": "5.2.11",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
- "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==",
- "dev": true,
- "dependencies": {
- "esbuild": "^0.20.1",
- "postcss": "^8.4.38",
- "rollup": "^4.13.0"
- },
- "bin": {
- "vite": "bin/vite.js"
- },
- "engines": {
- "node": "^18.0.0 || >=20.0.0"
- },
- "funding": {
- "url": "https://github.com/vitejs/vite?sponsor=1"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- },
- "peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
- "less": "*",
- "lightningcss": "^1.21.0",
- "sass": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.0"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- }
- }
- },
- "node_modules/ws": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
- "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": "^5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- },
- "node_modules/xmlhttprequest-ssl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
- "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
- "engines": {
- "node": ">=0.4.0"
- }
- }
- }
-}
diff --git a/painter-js/package.json b/painter-js/package.json
index 3688d4a..921d031 100644
--- a/painter-js/package.json
+++ b/painter-js/package.json
@@ -6,17 +6,19 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "fmt": "prettier --write 'src/**/*.{js,ts,css,html}'",
+ "fmt:check": "prettier --check 'src/**/*.{js,ts,css,html}'"
},
"devDependencies": {
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
+ "prettier": "^3.9.6",
"tailwindcss": "^3.4.3",
"typescript": "^5.2.2",
"vite": "^5.2.0"
},
"dependencies": {
- "socket.io-client": "^4.7.5",
- "three": "^0.164.1"
+ "socket.io-client": "^4.7.5"
}
}
diff --git a/painter-js/pnpm-lock.yaml b/painter-js/pnpm-lock.yaml
deleted file mode 100644
index 6819446..0000000
--- a/painter-js/pnpm-lock.yaml
+++ /dev/null
@@ -1,1466 +0,0 @@
-lockfileVersion: '9.0'
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
-importers:
-
- .:
- dependencies:
- socket.io-client:
- specifier: ^4.7.5
- version: 4.7.5
- three:
- specifier: ^0.164.1
- version: 0.164.1
- devDependencies:
- autoprefixer:
- specifier: ^10.4.19
- version: 10.4.19(postcss@8.4.38)
- postcss:
- specifier: ^8.4.38
- version: 8.4.38
- tailwindcss:
- specifier: ^3.4.3
- version: 3.4.3
- typescript:
- specifier: ^5.2.2
- version: 5.4.5
- vite:
- specifier: ^5.2.0
- version: 5.2.11
-
-packages:
-
- '@alloc/quick-lru@5.2.0':
- resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
- engines: {node: '>=10'}
-
- '@esbuild/aix-ppc64@0.20.2':
- resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
- engines: {node: '>=12'}
- cpu: [ppc64]
- os: [aix]
-
- '@esbuild/android-arm64@0.20.2':
- resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [android]
-
- '@esbuild/android-arm@0.20.2':
- resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/android-x64@0.20.2':
- resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [android]
-
- '@esbuild/darwin-arm64@0.20.2':
- resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [darwin]
-
- '@esbuild/darwin-x64@0.20.2':
- resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [darwin]
-
- '@esbuild/freebsd-arm64@0.20.2':
- resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [freebsd]
-
- '@esbuild/freebsd-x64@0.20.2':
- resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [freebsd]
-
- '@esbuild/linux-arm64@0.20.2':
- resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [linux]
-
- '@esbuild/linux-arm@0.20.2':
- resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [linux]
-
- '@esbuild/linux-ia32@0.20.2':
- resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [linux]
-
- '@esbuild/linux-loong64@0.20.2':
- resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
- engines: {node: '>=12'}
- cpu: [loong64]
- os: [linux]
-
- '@esbuild/linux-mips64el@0.20.2':
- resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
- engines: {node: '>=12'}
- cpu: [mips64el]
- os: [linux]
-
- '@esbuild/linux-ppc64@0.20.2':
- resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
- engines: {node: '>=12'}
- cpu: [ppc64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.20.2':
- resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
- engines: {node: '>=12'}
- cpu: [riscv64]
- os: [linux]
-
- '@esbuild/linux-s390x@0.20.2':
- resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
- engines: {node: '>=12'}
- cpu: [s390x]
- os: [linux]
-
- '@esbuild/linux-x64@0.20.2':
- resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [linux]
-
- '@esbuild/netbsd-x64@0.20.2':
- resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [netbsd]
-
- '@esbuild/openbsd-x64@0.20.2':
- resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [openbsd]
-
- '@esbuild/sunos-x64@0.20.2':
- resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [sunos]
-
- '@esbuild/win32-arm64@0.20.2':
- resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [win32]
-
- '@esbuild/win32-ia32@0.20.2':
- resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [win32]
-
- '@esbuild/win32-x64@0.20.2':
- resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [win32]
-
- '@isaacs/cliui@8.0.2':
- resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
- engines: {node: '>=12'}
-
- '@jridgewell/gen-mapping@0.3.5':
- resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/resolve-uri@3.1.2':
- resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/set-array@1.2.1':
- resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/sourcemap-codec@1.4.15':
- resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
-
- '@jridgewell/trace-mapping@0.3.25':
- resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
-
- '@nodelib/fs.scandir@2.1.5':
- resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
- engines: {node: '>= 8'}
-
- '@nodelib/fs.stat@2.0.5':
- resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
- engines: {node: '>= 8'}
-
- '@nodelib/fs.walk@1.2.8':
- resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
- engines: {node: '>= 8'}
-
- '@pkgjs/parseargs@0.11.0':
- resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
- engines: {node: '>=14'}
-
- '@rollup/rollup-android-arm-eabi@4.17.2':
- resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==}
- cpu: [arm]
- os: [android]
-
- '@rollup/rollup-android-arm64@4.17.2':
- resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==}
- cpu: [arm64]
- os: [android]
-
- '@rollup/rollup-darwin-arm64@4.17.2':
- resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==}
- cpu: [arm64]
- os: [darwin]
-
- '@rollup/rollup-darwin-x64@4.17.2':
- resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==}
- cpu: [x64]
- os: [darwin]
-
- '@rollup/rollup-linux-arm-gnueabihf@4.17.2':
- resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==}
- cpu: [arm]
- os: [linux]
-
- '@rollup/rollup-linux-arm-musleabihf@4.17.2':
- resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==}
- cpu: [arm]
- os: [linux]
-
- '@rollup/rollup-linux-arm64-gnu@4.17.2':
- resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==}
- cpu: [arm64]
- os: [linux]
-
- '@rollup/rollup-linux-arm64-musl@4.17.2':
- resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==}
- cpu: [arm64]
- os: [linux]
-
- '@rollup/rollup-linux-powerpc64le-gnu@4.17.2':
- resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==}
- cpu: [ppc64]
- os: [linux]
-
- '@rollup/rollup-linux-riscv64-gnu@4.17.2':
- resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==}
- cpu: [riscv64]
- os: [linux]
-
- '@rollup/rollup-linux-s390x-gnu@4.17.2':
- resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==}
- cpu: [s390x]
- os: [linux]
-
- '@rollup/rollup-linux-x64-gnu@4.17.2':
- resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==}
- cpu: [x64]
- os: [linux]
-
- '@rollup/rollup-linux-x64-musl@4.17.2':
- resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==}
- cpu: [x64]
- os: [linux]
-
- '@rollup/rollup-win32-arm64-msvc@4.17.2':
- resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==}
- cpu: [arm64]
- os: [win32]
-
- '@rollup/rollup-win32-ia32-msvc@4.17.2':
- resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==}
- cpu: [ia32]
- os: [win32]
-
- '@rollup/rollup-win32-x64-msvc@4.17.2':
- resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==}
- cpu: [x64]
- os: [win32]
-
- '@socket.io/component-emitter@3.1.2':
- resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
-
- '@types/estree@1.0.5':
- resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
-
- ansi-regex@5.0.1:
- resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
- engines: {node: '>=8'}
-
- ansi-regex@6.0.1:
- resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
- engines: {node: '>=12'}
-
- ansi-styles@4.3.0:
- resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
- engines: {node: '>=8'}
-
- ansi-styles@6.2.1:
- resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
- engines: {node: '>=12'}
-
- any-promise@1.3.0:
- resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
-
- anymatch@3.1.3:
- resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
- engines: {node: '>= 8'}
-
- arg@5.0.2:
- resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
-
- autoprefixer@10.4.19:
- resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==}
- engines: {node: ^10 || ^12 || >=14}
- hasBin: true
- peerDependencies:
- postcss: ^8.1.0
-
- balanced-match@1.0.2:
- resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
-
- binary-extensions@2.3.0:
- resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
- engines: {node: '>=8'}
-
- brace-expansion@2.0.1:
- resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
-
- braces@3.0.2:
- resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
- engines: {node: '>=8'}
-
- browserslist@4.23.0:
- resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
- engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
- hasBin: true
-
- camelcase-css@2.0.1:
- resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
- engines: {node: '>= 6'}
-
- caniuse-lite@1.0.30001618:
- resolution: {integrity: sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==}
-
- chokidar@3.6.0:
- resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
- engines: {node: '>= 8.10.0'}
-
- color-convert@2.0.1:
- resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
- engines: {node: '>=7.0.0'}
-
- color-name@1.1.4:
- resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-
- commander@4.1.1:
- resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
- engines: {node: '>= 6'}
-
- cross-spawn@7.0.3:
- resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
- engines: {node: '>= 8'}
-
- cssesc@3.0.0:
- resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
- engines: {node: '>=4'}
- hasBin: true
-
- debug@4.3.4:
- resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
- engines: {node: '>=6.0'}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
-
- didyoumean@1.2.2:
- resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
-
- dlv@1.1.3:
- resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
-
- eastasianwidth@0.2.0:
- resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
-
- electron-to-chromium@1.4.767:
- resolution: {integrity: sha512-nzzHfmQqBss7CE3apQHkHjXW77+8w3ubGCIoEijKCJebPufREaFETgGXWTkh32t259F3Kcq+R8MZdFdOJROgYw==}
-
- emoji-regex@8.0.0:
- resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
-
- emoji-regex@9.2.2:
- resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
-
- engine.io-client@6.5.3:
- resolution: {integrity: sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==}
-
- engine.io-parser@5.2.2:
- resolution: {integrity: sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==}
- engines: {node: '>=10.0.0'}
-
- esbuild@0.20.2:
- resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
- engines: {node: '>=12'}
- hasBin: true
-
- escalade@3.1.2:
- resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
- engines: {node: '>=6'}
-
- fast-glob@3.3.2:
- resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
- engines: {node: '>=8.6.0'}
-
- fastq@1.17.1:
- resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
-
- fill-range@7.0.1:
- resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
- engines: {node: '>=8'}
-
- foreground-child@3.1.1:
- resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
- engines: {node: '>=14'}
-
- fraction.js@4.3.7:
- resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
-
- fsevents@2.3.3:
- resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
- engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
- os: [darwin]
-
- function-bind@1.1.2:
- resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
-
- glob-parent@5.1.2:
- resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
- engines: {node: '>= 6'}
-
- glob-parent@6.0.2:
- resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
- engines: {node: '>=10.13.0'}
-
- glob@10.3.15:
- resolution: {integrity: sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==}
- engines: {node: '>=16 || 14 >=14.18'}
- hasBin: true
-
- hasown@2.0.2:
- resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
- engines: {node: '>= 0.4'}
-
- is-binary-path@2.1.0:
- resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
- engines: {node: '>=8'}
-
- is-core-module@2.13.1:
- resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
-
- is-extglob@2.1.1:
- resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
- engines: {node: '>=0.10.0'}
-
- is-fullwidth-code-point@3.0.0:
- resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
- engines: {node: '>=8'}
-
- is-glob@4.0.3:
- resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
- engines: {node: '>=0.10.0'}
-
- is-number@7.0.0:
- resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
- engines: {node: '>=0.12.0'}
-
- isexe@2.0.0:
- resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
-
- jackspeak@2.3.6:
- resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
- engines: {node: '>=14'}
-
- jiti@1.21.0:
- resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==}
- hasBin: true
-
- lilconfig@2.1.0:
- resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
- engines: {node: '>=10'}
-
- lilconfig@3.1.1:
- resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==}
- engines: {node: '>=14'}
-
- lines-and-columns@1.2.4:
- resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
-
- lru-cache@10.2.2:
- resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==}
- engines: {node: 14 || >=16.14}
-
- merge2@1.4.1:
- resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
- engines: {node: '>= 8'}
-
- micromatch@4.0.5:
- resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
- engines: {node: '>=8.6'}
-
- minimatch@9.0.4:
- resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
- engines: {node: '>=16 || 14 >=14.17'}
-
- minipass@7.1.1:
- resolution: {integrity: sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==}
- engines: {node: '>=16 || 14 >=14.17'}
-
- ms@2.1.2:
- resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
-
- mz@2.7.0:
- resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
-
- nanoid@3.3.7:
- resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
- node-releases@2.0.14:
- resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
-
- normalize-path@3.0.0:
- resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
- engines: {node: '>=0.10.0'}
-
- normalize-range@0.1.2:
- resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
- engines: {node: '>=0.10.0'}
-
- object-assign@4.1.1:
- resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
- engines: {node: '>=0.10.0'}
-
- object-hash@3.0.0:
- resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
- engines: {node: '>= 6'}
-
- path-key@3.1.1:
- resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
- engines: {node: '>=8'}
-
- path-parse@1.0.7:
- resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
-
- path-scurry@1.11.1:
- resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
- engines: {node: '>=16 || 14 >=14.18'}
-
- picocolors@1.0.1:
- resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
-
- picomatch@2.3.1:
- resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
- engines: {node: '>=8.6'}
-
- pify@2.3.0:
- resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
- engines: {node: '>=0.10.0'}
-
- pirates@4.0.6:
- resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
- engines: {node: '>= 6'}
-
- postcss-import@15.1.0:
- resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
- engines: {node: '>=14.0.0'}
- peerDependencies:
- postcss: ^8.0.0
-
- postcss-js@4.0.1:
- resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
- engines: {node: ^12 || ^14 || >= 16}
- peerDependencies:
- postcss: ^8.4.21
-
- postcss-load-config@4.0.2:
- resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
- engines: {node: '>= 14'}
- peerDependencies:
- postcss: '>=8.0.9'
- ts-node: '>=9.0.0'
- peerDependenciesMeta:
- postcss:
- optional: true
- ts-node:
- optional: true
-
- postcss-nested@6.0.1:
- resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
- engines: {node: '>=12.0'}
- peerDependencies:
- postcss: ^8.2.14
-
- postcss-selector-parser@6.0.16:
- resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==}
- engines: {node: '>=4'}
-
- postcss-value-parser@4.2.0:
- resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
-
- postcss@8.4.38:
- resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
- engines: {node: ^10 || ^12 || >=14}
-
- queue-microtask@1.2.3:
- resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
-
- read-cache@1.0.0:
- resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
-
- readdirp@3.6.0:
- resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
- engines: {node: '>=8.10.0'}
-
- resolve@1.22.8:
- resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
- hasBin: true
-
- reusify@1.0.4:
- resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
- engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
-
- rollup@4.17.2:
- resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==}
- engines: {node: '>=18.0.0', npm: '>=8.0.0'}
- hasBin: true
-
- run-parallel@1.2.0:
- resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
-
- shebang-command@2.0.0:
- resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
- engines: {node: '>=8'}
-
- shebang-regex@3.0.0:
- resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
- engines: {node: '>=8'}
-
- signal-exit@4.1.0:
- resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
- engines: {node: '>=14'}
-
- socket.io-client@4.7.5:
- resolution: {integrity: sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==}
- engines: {node: '>=10.0.0'}
-
- socket.io-parser@4.2.4:
- resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
- engines: {node: '>=10.0.0'}
-
- source-map-js@1.2.0:
- resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
- engines: {node: '>=0.10.0'}
-
- string-width@4.2.3:
- resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
- engines: {node: '>=8'}
-
- string-width@5.1.2:
- resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
- engines: {node: '>=12'}
-
- strip-ansi@6.0.1:
- resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
- engines: {node: '>=8'}
-
- strip-ansi@7.1.0:
- resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
- engines: {node: '>=12'}
-
- sucrase@3.35.0:
- resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
- engines: {node: '>=16 || 14 >=14.17'}
- hasBin: true
-
- supports-preserve-symlinks-flag@1.0.0:
- resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
- engines: {node: '>= 0.4'}
-
- tailwindcss@3.4.3:
- resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==}
- engines: {node: '>=14.0.0'}
- hasBin: true
-
- thenify-all@1.6.0:
- resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
- engines: {node: '>=0.8'}
-
- thenify@3.3.1:
- resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
-
- three@0.164.1:
- resolution: {integrity: sha512-iC/hUBbl1vzFny7f5GtqzVXYjMJKaTPxiCxXfrvVdBi1Sf+jhd1CAkitiFwC7mIBFCo3MrDLJG97yisoaWig0w==}
-
- to-regex-range@5.0.1:
- resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
- engines: {node: '>=8.0'}
-
- ts-interface-checker@0.1.13:
- resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
-
- typescript@5.4.5:
- resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
- engines: {node: '>=14.17'}
- hasBin: true
-
- update-browserslist-db@1.0.16:
- resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==}
- hasBin: true
- peerDependencies:
- browserslist: '>= 4.21.0'
-
- util-deprecate@1.0.2:
- resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
-
- vite@5.2.11:
- resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==}
- engines: {node: ^18.0.0 || >=20.0.0}
- hasBin: true
- peerDependencies:
- '@types/node': ^18.0.0 || >=20.0.0
- less: '*'
- lightningcss: ^1.21.0
- sass: '*'
- stylus: '*'
- sugarss: '*'
- terser: ^5.4.0
- peerDependenciesMeta:
- '@types/node':
- optional: true
- less:
- optional: true
- lightningcss:
- optional: true
- sass:
- optional: true
- stylus:
- optional: true
- sugarss:
- optional: true
- terser:
- optional: true
-
- which@2.0.2:
- resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
- engines: {node: '>= 8'}
- hasBin: true
-
- wrap-ansi@7.0.0:
- resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
- engines: {node: '>=10'}
-
- wrap-ansi@8.1.0:
- resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
- engines: {node: '>=12'}
-
- ws@8.11.0:
- resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==}
- engines: {node: '>=10.0.0'}
- peerDependencies:
- bufferutil: ^4.0.1
- utf-8-validate: ^5.0.2
- peerDependenciesMeta:
- bufferutil:
- optional: true
- utf-8-validate:
- optional: true
-
- xmlhttprequest-ssl@2.0.0:
- resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==}
- engines: {node: '>=0.4.0'}
-
- yaml@2.4.2:
- resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==}
- engines: {node: '>= 14'}
- hasBin: true
-
-snapshots:
-
- '@alloc/quick-lru@5.2.0': {}
-
- '@esbuild/aix-ppc64@0.20.2':
- optional: true
-
- '@esbuild/android-arm64@0.20.2':
- optional: true
-
- '@esbuild/android-arm@0.20.2':
- optional: true
-
- '@esbuild/android-x64@0.20.2':
- optional: true
-
- '@esbuild/darwin-arm64@0.20.2':
- optional: true
-
- '@esbuild/darwin-x64@0.20.2':
- optional: true
-
- '@esbuild/freebsd-arm64@0.20.2':
- optional: true
-
- '@esbuild/freebsd-x64@0.20.2':
- optional: true
-
- '@esbuild/linux-arm64@0.20.2':
- optional: true
-
- '@esbuild/linux-arm@0.20.2':
- optional: true
-
- '@esbuild/linux-ia32@0.20.2':
- optional: true
-
- '@esbuild/linux-loong64@0.20.2':
- optional: true
-
- '@esbuild/linux-mips64el@0.20.2':
- optional: true
-
- '@esbuild/linux-ppc64@0.20.2':
- optional: true
-
- '@esbuild/linux-riscv64@0.20.2':
- optional: true
-
- '@esbuild/linux-s390x@0.20.2':
- optional: true
-
- '@esbuild/linux-x64@0.20.2':
- optional: true
-
- '@esbuild/netbsd-x64@0.20.2':
- optional: true
-
- '@esbuild/openbsd-x64@0.20.2':
- optional: true
-
- '@esbuild/sunos-x64@0.20.2':
- optional: true
-
- '@esbuild/win32-arm64@0.20.2':
- optional: true
-
- '@esbuild/win32-ia32@0.20.2':
- optional: true
-
- '@esbuild/win32-x64@0.20.2':
- optional: true
-
- '@isaacs/cliui@8.0.2':
- dependencies:
- string-width: 5.1.2
- string-width-cjs: string-width@4.2.3
- strip-ansi: 7.1.0
- strip-ansi-cjs: strip-ansi@6.0.1
- wrap-ansi: 8.1.0
- wrap-ansi-cjs: wrap-ansi@7.0.0
-
- '@jridgewell/gen-mapping@0.3.5':
- dependencies:
- '@jridgewell/set-array': 1.2.1
- '@jridgewell/sourcemap-codec': 1.4.15
- '@jridgewell/trace-mapping': 0.3.25
-
- '@jridgewell/resolve-uri@3.1.2': {}
-
- '@jridgewell/set-array@1.2.1': {}
-
- '@jridgewell/sourcemap-codec@1.4.15': {}
-
- '@jridgewell/trace-mapping@0.3.25':
- dependencies:
- '@jridgewell/resolve-uri': 3.1.2
- '@jridgewell/sourcemap-codec': 1.4.15
-
- '@nodelib/fs.scandir@2.1.5':
- dependencies:
- '@nodelib/fs.stat': 2.0.5
- run-parallel: 1.2.0
-
- '@nodelib/fs.stat@2.0.5': {}
-
- '@nodelib/fs.walk@1.2.8':
- dependencies:
- '@nodelib/fs.scandir': 2.1.5
- fastq: 1.17.1
-
- '@pkgjs/parseargs@0.11.0':
- optional: true
-
- '@rollup/rollup-android-arm-eabi@4.17.2':
- optional: true
-
- '@rollup/rollup-android-arm64@4.17.2':
- optional: true
-
- '@rollup/rollup-darwin-arm64@4.17.2':
- optional: true
-
- '@rollup/rollup-darwin-x64@4.17.2':
- optional: true
-
- '@rollup/rollup-linux-arm-gnueabihf@4.17.2':
- optional: true
-
- '@rollup/rollup-linux-arm-musleabihf@4.17.2':
- optional: true
-
- '@rollup/rollup-linux-arm64-gnu@4.17.2':
- optional: true
-
- '@rollup/rollup-linux-arm64-musl@4.17.2':
- optional: true
-
- '@rollup/rollup-linux-powerpc64le-gnu@4.17.2':
- optional: true
-
- '@rollup/rollup-linux-riscv64-gnu@4.17.2':
- optional: true
-
- '@rollup/rollup-linux-s390x-gnu@4.17.2':
- optional: true
-
- '@rollup/rollup-linux-x64-gnu@4.17.2':
- optional: true
-
- '@rollup/rollup-linux-x64-musl@4.17.2':
- optional: true
-
- '@rollup/rollup-win32-arm64-msvc@4.17.2':
- optional: true
-
- '@rollup/rollup-win32-ia32-msvc@4.17.2':
- optional: true
-
- '@rollup/rollup-win32-x64-msvc@4.17.2':
- optional: true
-
- '@socket.io/component-emitter@3.1.2': {}
-
- '@types/estree@1.0.5': {}
-
- ansi-regex@5.0.1: {}
-
- ansi-regex@6.0.1: {}
-
- ansi-styles@4.3.0:
- dependencies:
- color-convert: 2.0.1
-
- ansi-styles@6.2.1: {}
-
- any-promise@1.3.0: {}
-
- anymatch@3.1.3:
- dependencies:
- normalize-path: 3.0.0
- picomatch: 2.3.1
-
- arg@5.0.2: {}
-
- autoprefixer@10.4.19(postcss@8.4.38):
- dependencies:
- browserslist: 4.23.0
- caniuse-lite: 1.0.30001618
- fraction.js: 4.3.7
- normalize-range: 0.1.2
- picocolors: 1.0.1
- postcss: 8.4.38
- postcss-value-parser: 4.2.0
-
- balanced-match@1.0.2: {}
-
- binary-extensions@2.3.0: {}
-
- brace-expansion@2.0.1:
- dependencies:
- balanced-match: 1.0.2
-
- braces@3.0.2:
- dependencies:
- fill-range: 7.0.1
-
- browserslist@4.23.0:
- dependencies:
- caniuse-lite: 1.0.30001618
- electron-to-chromium: 1.4.767
- node-releases: 2.0.14
- update-browserslist-db: 1.0.16(browserslist@4.23.0)
-
- camelcase-css@2.0.1: {}
-
- caniuse-lite@1.0.30001618: {}
-
- chokidar@3.6.0:
- dependencies:
- anymatch: 3.1.3
- braces: 3.0.2
- glob-parent: 5.1.2
- is-binary-path: 2.1.0
- is-glob: 4.0.3
- normalize-path: 3.0.0
- readdirp: 3.6.0
- optionalDependencies:
- fsevents: 2.3.3
-
- color-convert@2.0.1:
- dependencies:
- color-name: 1.1.4
-
- color-name@1.1.4: {}
-
- commander@4.1.1: {}
-
- cross-spawn@7.0.3:
- dependencies:
- path-key: 3.1.1
- shebang-command: 2.0.0
- which: 2.0.2
-
- cssesc@3.0.0: {}
-
- debug@4.3.4:
- dependencies:
- ms: 2.1.2
-
- didyoumean@1.2.2: {}
-
- dlv@1.1.3: {}
-
- eastasianwidth@0.2.0: {}
-
- electron-to-chromium@1.4.767: {}
-
- emoji-regex@8.0.0: {}
-
- emoji-regex@9.2.2: {}
-
- engine.io-client@6.5.3:
- dependencies:
- '@socket.io/component-emitter': 3.1.2
- debug: 4.3.4
- engine.io-parser: 5.2.2
- ws: 8.11.0
- xmlhttprequest-ssl: 2.0.0
- transitivePeerDependencies:
- - bufferutil
- - supports-color
- - utf-8-validate
-
- engine.io-parser@5.2.2: {}
-
- esbuild@0.20.2:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.20.2
- '@esbuild/android-arm': 0.20.2
- '@esbuild/android-arm64': 0.20.2
- '@esbuild/android-x64': 0.20.2
- '@esbuild/darwin-arm64': 0.20.2
- '@esbuild/darwin-x64': 0.20.2
- '@esbuild/freebsd-arm64': 0.20.2
- '@esbuild/freebsd-x64': 0.20.2
- '@esbuild/linux-arm': 0.20.2
- '@esbuild/linux-arm64': 0.20.2
- '@esbuild/linux-ia32': 0.20.2
- '@esbuild/linux-loong64': 0.20.2
- '@esbuild/linux-mips64el': 0.20.2
- '@esbuild/linux-ppc64': 0.20.2
- '@esbuild/linux-riscv64': 0.20.2
- '@esbuild/linux-s390x': 0.20.2
- '@esbuild/linux-x64': 0.20.2
- '@esbuild/netbsd-x64': 0.20.2
- '@esbuild/openbsd-x64': 0.20.2
- '@esbuild/sunos-x64': 0.20.2
- '@esbuild/win32-arm64': 0.20.2
- '@esbuild/win32-ia32': 0.20.2
- '@esbuild/win32-x64': 0.20.2
-
- escalade@3.1.2: {}
-
- fast-glob@3.3.2:
- dependencies:
- '@nodelib/fs.stat': 2.0.5
- '@nodelib/fs.walk': 1.2.8
- glob-parent: 5.1.2
- merge2: 1.4.1
- micromatch: 4.0.5
-
- fastq@1.17.1:
- dependencies:
- reusify: 1.0.4
-
- fill-range@7.0.1:
- dependencies:
- to-regex-range: 5.0.1
-
- foreground-child@3.1.1:
- dependencies:
- cross-spawn: 7.0.3
- signal-exit: 4.1.0
-
- fraction.js@4.3.7: {}
-
- fsevents@2.3.3:
- optional: true
-
- function-bind@1.1.2: {}
-
- glob-parent@5.1.2:
- dependencies:
- is-glob: 4.0.3
-
- glob-parent@6.0.2:
- dependencies:
- is-glob: 4.0.3
-
- glob@10.3.15:
- dependencies:
- foreground-child: 3.1.1
- jackspeak: 2.3.6
- minimatch: 9.0.4
- minipass: 7.1.1
- path-scurry: 1.11.1
-
- hasown@2.0.2:
- dependencies:
- function-bind: 1.1.2
-
- is-binary-path@2.1.0:
- dependencies:
- binary-extensions: 2.3.0
-
- is-core-module@2.13.1:
- dependencies:
- hasown: 2.0.2
-
- is-extglob@2.1.1: {}
-
- is-fullwidth-code-point@3.0.0: {}
-
- is-glob@4.0.3:
- dependencies:
- is-extglob: 2.1.1
-
- is-number@7.0.0: {}
-
- isexe@2.0.0: {}
-
- jackspeak@2.3.6:
- dependencies:
- '@isaacs/cliui': 8.0.2
- optionalDependencies:
- '@pkgjs/parseargs': 0.11.0
-
- jiti@1.21.0: {}
-
- lilconfig@2.1.0: {}
-
- lilconfig@3.1.1: {}
-
- lines-and-columns@1.2.4: {}
-
- lru-cache@10.2.2: {}
-
- merge2@1.4.1: {}
-
- micromatch@4.0.5:
- dependencies:
- braces: 3.0.2
- picomatch: 2.3.1
-
- minimatch@9.0.4:
- dependencies:
- brace-expansion: 2.0.1
-
- minipass@7.1.1: {}
-
- ms@2.1.2: {}
-
- mz@2.7.0:
- dependencies:
- any-promise: 1.3.0
- object-assign: 4.1.1
- thenify-all: 1.6.0
-
- nanoid@3.3.7: {}
-
- node-releases@2.0.14: {}
-
- normalize-path@3.0.0: {}
-
- normalize-range@0.1.2: {}
-
- object-assign@4.1.1: {}
-
- object-hash@3.0.0: {}
-
- path-key@3.1.1: {}
-
- path-parse@1.0.7: {}
-
- path-scurry@1.11.1:
- dependencies:
- lru-cache: 10.2.2
- minipass: 7.1.1
-
- picocolors@1.0.1: {}
-
- picomatch@2.3.1: {}
-
- pify@2.3.0: {}
-
- pirates@4.0.6: {}
-
- postcss-import@15.1.0(postcss@8.4.38):
- dependencies:
- postcss: 8.4.38
- postcss-value-parser: 4.2.0
- read-cache: 1.0.0
- resolve: 1.22.8
-
- postcss-js@4.0.1(postcss@8.4.38):
- dependencies:
- camelcase-css: 2.0.1
- postcss: 8.4.38
-
- postcss-load-config@4.0.2(postcss@8.4.38):
- dependencies:
- lilconfig: 3.1.1
- yaml: 2.4.2
- optionalDependencies:
- postcss: 8.4.38
-
- postcss-nested@6.0.1(postcss@8.4.38):
- dependencies:
- postcss: 8.4.38
- postcss-selector-parser: 6.0.16
-
- postcss-selector-parser@6.0.16:
- dependencies:
- cssesc: 3.0.0
- util-deprecate: 1.0.2
-
- postcss-value-parser@4.2.0: {}
-
- postcss@8.4.38:
- dependencies:
- nanoid: 3.3.7
- picocolors: 1.0.1
- source-map-js: 1.2.0
-
- queue-microtask@1.2.3: {}
-
- read-cache@1.0.0:
- dependencies:
- pify: 2.3.0
-
- readdirp@3.6.0:
- dependencies:
- picomatch: 2.3.1
-
- resolve@1.22.8:
- dependencies:
- is-core-module: 2.13.1
- path-parse: 1.0.7
- supports-preserve-symlinks-flag: 1.0.0
-
- reusify@1.0.4: {}
-
- rollup@4.17.2:
- dependencies:
- '@types/estree': 1.0.5
- optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.17.2
- '@rollup/rollup-android-arm64': 4.17.2
- '@rollup/rollup-darwin-arm64': 4.17.2
- '@rollup/rollup-darwin-x64': 4.17.2
- '@rollup/rollup-linux-arm-gnueabihf': 4.17.2
- '@rollup/rollup-linux-arm-musleabihf': 4.17.2
- '@rollup/rollup-linux-arm64-gnu': 4.17.2
- '@rollup/rollup-linux-arm64-musl': 4.17.2
- '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2
- '@rollup/rollup-linux-riscv64-gnu': 4.17.2
- '@rollup/rollup-linux-s390x-gnu': 4.17.2
- '@rollup/rollup-linux-x64-gnu': 4.17.2
- '@rollup/rollup-linux-x64-musl': 4.17.2
- '@rollup/rollup-win32-arm64-msvc': 4.17.2
- '@rollup/rollup-win32-ia32-msvc': 4.17.2
- '@rollup/rollup-win32-x64-msvc': 4.17.2
- fsevents: 2.3.3
-
- run-parallel@1.2.0:
- dependencies:
- queue-microtask: 1.2.3
-
- shebang-command@2.0.0:
- dependencies:
- shebang-regex: 3.0.0
-
- shebang-regex@3.0.0: {}
-
- signal-exit@4.1.0: {}
-
- socket.io-client@4.7.5:
- dependencies:
- '@socket.io/component-emitter': 3.1.2
- debug: 4.3.4
- engine.io-client: 6.5.3
- socket.io-parser: 4.2.4
- transitivePeerDependencies:
- - bufferutil
- - supports-color
- - utf-8-validate
-
- socket.io-parser@4.2.4:
- dependencies:
- '@socket.io/component-emitter': 3.1.2
- debug: 4.3.4
- transitivePeerDependencies:
- - supports-color
-
- source-map-js@1.2.0: {}
-
- string-width@4.2.3:
- dependencies:
- emoji-regex: 8.0.0
- is-fullwidth-code-point: 3.0.0
- strip-ansi: 6.0.1
-
- string-width@5.1.2:
- dependencies:
- eastasianwidth: 0.2.0
- emoji-regex: 9.2.2
- strip-ansi: 7.1.0
-
- strip-ansi@6.0.1:
- dependencies:
- ansi-regex: 5.0.1
-
- strip-ansi@7.1.0:
- dependencies:
- ansi-regex: 6.0.1
-
- sucrase@3.35.0:
- dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- commander: 4.1.1
- glob: 10.3.15
- lines-and-columns: 1.2.4
- mz: 2.7.0
- pirates: 4.0.6
- ts-interface-checker: 0.1.13
-
- supports-preserve-symlinks-flag@1.0.0: {}
-
- tailwindcss@3.4.3:
- dependencies:
- '@alloc/quick-lru': 5.2.0
- arg: 5.0.2
- chokidar: 3.6.0
- didyoumean: 1.2.2
- dlv: 1.1.3
- fast-glob: 3.3.2
- glob-parent: 6.0.2
- is-glob: 4.0.3
- jiti: 1.21.0
- lilconfig: 2.1.0
- micromatch: 4.0.5
- normalize-path: 3.0.0
- object-hash: 3.0.0
- picocolors: 1.0.1
- postcss: 8.4.38
- postcss-import: 15.1.0(postcss@8.4.38)
- postcss-js: 4.0.1(postcss@8.4.38)
- postcss-load-config: 4.0.2(postcss@8.4.38)
- postcss-nested: 6.0.1(postcss@8.4.38)
- postcss-selector-parser: 6.0.16
- resolve: 1.22.8
- sucrase: 3.35.0
- transitivePeerDependencies:
- - ts-node
-
- thenify-all@1.6.0:
- dependencies:
- thenify: 3.3.1
-
- thenify@3.3.1:
- dependencies:
- any-promise: 1.3.0
-
- three@0.164.1: {}
-
- to-regex-range@5.0.1:
- dependencies:
- is-number: 7.0.0
-
- ts-interface-checker@0.1.13: {}
-
- typescript@5.4.5: {}
-
- update-browserslist-db@1.0.16(browserslist@4.23.0):
- dependencies:
- browserslist: 4.23.0
- escalade: 3.1.2
- picocolors: 1.0.1
-
- util-deprecate@1.0.2: {}
-
- vite@5.2.11:
- dependencies:
- esbuild: 0.20.2
- postcss: 8.4.38
- rollup: 4.17.2
- optionalDependencies:
- fsevents: 2.3.3
-
- which@2.0.2:
- dependencies:
- isexe: 2.0.0
-
- wrap-ansi@7.0.0:
- dependencies:
- ansi-styles: 4.3.0
- string-width: 4.2.3
- strip-ansi: 6.0.1
-
- wrap-ansi@8.1.0:
- dependencies:
- ansi-styles: 6.2.1
- string-width: 5.1.2
- strip-ansi: 7.1.0
-
- ws@8.11.0: {}
-
- xmlhttprequest-ssl@2.0.0: {}
-
- yaml@2.4.2: {}
diff --git a/painter-js/public/og-image.png b/painter-js/public/og-image.png
new file mode 100644
index 0000000..e2576d1
Binary files /dev/null and b/painter-js/public/og-image.png differ
diff --git a/painter-js/src/Astronaut.glb b/painter-js/src/Astronaut.glb
deleted file mode 100644
index b0b85d7..0000000
Binary files a/painter-js/src/Astronaut.glb and /dev/null differ
diff --git a/painter-js/src/canvas.js b/painter-js/src/canvas.js
deleted file mode 100644
index 31366d3..0000000
--- a/painter-js/src/canvas.js
+++ /dev/null
@@ -1,267 +0,0 @@
-import {
- hexToU32,
- u32ToHex,
- getColorFromElementCSS,
- rgbToHex,
-} from "./utils.js";
-import {
- pixelSize,
- pixelCooldown,
- canvasEndpoint,
- WIDTH,
- HEIGHT,
-} from "./constants.js";
-
-let socket = null;
-
-const canvas = document.getElementById("canvas");
-const ctx = canvas.getContext("2d");
-const countdownDiv = document.getElementById("countdown");
-let lastPixelTime = parseInt(localStorage.getItem("lastPixelTime") || "0");
-
-const colorPicker = document.getElementById("color-picker");
-const redButton = document.getElementById("red");
-const greenButton = document.getElementById("green");
-const blueButton = document.getElementById("blue");
-const yellowButton = document.getElementById("yellow");
-const purpleButton = document.getElementById("purple");
-const pinkButton = document.getElementById("pink");
-const cyanButton = document.getElementById("cyan");
-const whiteButton = document.getElementById("white");
-const blackButton = document.getElementById("black");
-const orangeButton = document.getElementById("orange");
-const brownButton = document.getElementById("brown");
-
-const currentColorSpan = document.getElementById("current-color-span");
-const toggleGridToggle = document.getElementById("toggle-grid");
-const placePixelButton = document.getElementById("place-pixel");
-const saveCanvasButton = document.getElementById("save-canvas");
-
-colorPicker.value = localStorage.getItem("currentColor") || "#000000";
-
-let currentColor = colorPicker.value;
-let showGrid = toggleGridToggle.checked;
-currentColorSpan.style.backgroundColor = currentColor;
-
-let canvasState = [];
-let confirmPlacePixel = false;
-let previewPixel = null;
-
-const setCurrentColor = (color, isColorPicker = false) => {
- const hexColor = isColorPicker ? color : rgbToHex(color);
- currentColor = hexColor;
- currentColorSpan.style.backgroundColor = hexColor;
- localStorage.setItem("currentColor", hexColor);
-};
-
-const handleColorPicker = () => {
- colorPicker.addEventListener("input", (event) => {
- setCurrentColor(event.target.value, true);
- });
-
- redButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(redButton));
- });
-
- greenButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(greenButton));
- });
-
- blueButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(blueButton));
- });
-
- yellowButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(yellowButton));
- });
-
- purpleButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(purpleButton));
- });
-
- pinkButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(pinkButton));
- });
-
- cyanButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(cyanButton));
- });
-
- whiteButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(whiteButton));
- });
-
- blackButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(blackButton));
- });
-
- orangeButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(orangeButton));
- });
-
- brownButton.addEventListener("click", () => {
- setCurrentColor(getColorFromElementCSS(brownButton));
- });
-};
-
-const fetchCanvasState = async () => {
- fetch(canvasEndpoint)
- .then((response) => response.json())
- .then((data) => {
- canvasState = data;
- drawCanvasState(data);
- })
- .catch((error) => {
- alert("Error fetching canvas state from server. Please try again later.");
- });
-};
-
-const drawCanvasState = (canvasState) => {
- for (let y = 0; y < HEIGHT; y++) {
- for (let x = 0; x < WIDTH; x++) {
- const index = y * WIDTH + x;
- const color = u32ToHex(canvasState[index]);
- ctx.fillStyle = color;
- ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
- }
- }
-};
-
-const checkIfCanPlacePixel = () => {
- const now = Date.now();
- return now - lastPixelTime >= pixelCooldown;
-};
-
-const setLastPixelTime = () => {
- lastPixelTime = Date.now();
- localStorage.setItem("lastPixelTime", lastPixelTime.toString());
-};
-
-const handlePlacePixel = (pixelData) => {
- if (!checkIfCanPlacePixel()) {
- alert("You can't place a pixel yet");
- return;
- }
-
- socket.emit("place-pixel", pixelData);
- const index = pixelData.y * WIDTH + pixelData.x;
- canvasState[index] = pixelData.color;
- setLastPixelTime();
- pixelData = null;
-};
-
-canvas.addEventListener("click", (event) => {
- const rect = canvas.getBoundingClientRect();
- const x = Math.floor((event.clientX - rect.left) / pixelSize);
- const y = Math.floor((event.clientY - rect.top) / pixelSize);
-
- const color = hexToU32(currentColor);
- const oldPreviewPixel = previewPixel;
-
- const update = { x, y, color };
-
- if (confirmPlacePixel) {
- handlePlacePixel(update);
- } else {
- previewPixel = update;
- }
-
- if (previewPixel) {
- if (oldPreviewPixel) {
- ctx.clearRect(
- oldPreviewPixel.x * pixelSize,
- oldPreviewPixel.y * pixelSize,
- pixelSize,
- pixelSize
- );
- }
-
- ctx.fillStyle = `rgba(${(color >> 16) & 0xff}, ${(color >> 8) & 0xff}, ${
- color & 0xff
- }, 0.5)`;
- ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
- }
-});
-
-const removePreviewPixel = () => {
- previewPixel = null;
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- drawCanvasState(canvasState);
-};
-
-const drawGrid = () => {
- ctx.strokeStyle = "#000";
- for (let x = 0; x < canvas.width; x += pixelSize) {
- ctx.beginPath();
- ctx.moveTo(x, 0);
- ctx.lineTo(x, canvas.height);
- ctx.stroke();
- }
- for (let y = 0; y < canvas.height; y += pixelSize) {
- ctx.beginPath();
- ctx.moveTo(0, y);
- ctx.lineTo(canvas.width, y);
- ctx.stroke();
- }
-};
-
-const handleToggleGrid = () => {
- toggleGridToggle.addEventListener("change", (event) => {
- showGrid = event.target.checked;
- localStorage.setItem("showGrid", showGrid);
- if (showGrid) {
- drawGrid();
- } else {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- drawCanvasState(canvasState);
- }
- });
-};
-
-window.onkeydown = (event) => {
- // on enter (keycode 13 is enter)
- if (event.keyCode === 13) {
- if (previewPixel) {
- handlePlacePixel(previewPixel);
- removePreviewPixel();
- }
- }
-};
-
-placePixelButton.addEventListener("click", () => {
- if (previewPixel) {
- handlePlacePixel(previewPixel);
- removePreviewPixel();
- }
-});
-
-saveCanvasButton.addEventListener("click", () => {
- const a = document.createElement("a");
- a.href = canvas.toDataURL();
- a.download = "canvas.png";
- a.click();
-});
-
-handleColorPicker();
-handleToggleGrid();
-
-export const handleSocketEvents = (_socket) => {
- socket = _socket;
- socket.on("connect", () => {
- fetchCanvasState();
- });
-
- socket.on("pixel-updated", (update) => {
- const color = u32ToHex(update.color);
- ctx.fillStyle = color;
- ctx.fillRect(
- update.x * pixelSize,
- update.y * pixelSize,
- pixelSize,
- pixelSize
- );
-
- const index = update.y * WIDTH + update.x;
- canvasState[index] = update.color;
- });
-};
diff --git a/painter-js/src/challenge.js b/painter-js/src/challenge.js
deleted file mode 100644
index e6494df..0000000
--- a/painter-js/src/challenge.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import * as three from 'three';
-import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
-import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
-
-let scene, camera, renderer, character, mixer, clock;
-let walkAction, idleAction;
-let moving = false;
-const keys = {}
-
-let boxes = [];
-
-const container = document.getElementById('challenge');
-
-const onWindowResize = () => {
- camera.aspect = container.clientWidth / container.clientHeight;
- camera.updateProjectionMatrix();
- renderer.setSize(container.clientWidth, container.clientHeight);
-}
-
-const onKeyDown = (e) => {
- keys[e.code] = true;
-}
-
-const onKeyUp = (e) => {
- keys[e.code] = false;
-}
-
-const init = () => {
- scene = new three.Scene();
- camera = new three.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
- renderer = new three.WebGLRenderer({ antialias: true});
- renderer.setSize(window.innerWidth, window.innerHeight);
- container.appendChild(renderer.domElement);
-
- clock = new three.Clock();
-
- // Adjust light positions and intensity
- const light = new three.DirectionalLight(0xffffff, 1);
- light.position.set(5, 10, 7.5);
- light.castShadow = true;
- scene.add(light);
-
-
-
- for (let i = 0; i < 10; i++) {
- const box = new three.Mesh(
- new three.BoxGeometry(1, 1, 1),
- new three.MeshStandardMaterial({ color: 0x00ff00 })
- );
- box.position.set(Math.random() * 10 - 5, 0.5, Math.random() * 10 - 5);
- box.castShadow = true;
- scene.add(box);
- boxes.push(box);
- }
-
- const loader = new GLTFLoader();
- loader.load('src/Astronaut.glb',(gltf) => {
- character = gltf.scene;
- scene.add(character);
-
- mixer = new three.AnimationMixer(character);
- gltf.animations.forEach((clip) => {
- if (clip.name === 'CharacterArmature|Walk') {
- walkAction = mixer.clipAction(clip);
- }
- if (clip.name === 'CharacterArmature|Idle') {
- idleAction = mixer.clipAction(clip);
- }
- });
-
- if (walkAction) walkAction.play();
- if (idleAction) idleAction.play();
-
- character.position.set(-10, 0, -60);
- character.rotation.y = Math.PI;
- })
-
- //blue sky
- scene.background = new three.Color(0x87ceeb);
-
- camera.position.set(20, 10, 10)
- camera.lookAt(0, 10, 0)
-
- window.addEventListener('resize', onWindowResize, false);
- document.addEventListener('keydown', onKeyDown, false);
- document.addEventListener('keyup', onKeyUp, false);
-}
-
-const animate = () => {
- requestAnimationFrame(animate);
-
- const delta = clock.getDelta();
- if (mixer) mixer.update(delta);
-
- if (character) {
- if (keys['KeyW']) {
- character.position.z -= 0.1;
- character.rotation.y = Math.PI; // North
- }
-
- if (keys['KeyS']) {
- character.position.z += 0.1;
- character.rotation.y = 0; // South
- }
-
- if (keys['KeyA']) {
- character.position.x -= 0.1;
- character.rotation.y = Math.PI / 2; // West
- }
-
- if (keys['KeyD']) {
- character.position.x += 0.1;
- character.rotation.y = -Math.PI / 2; // East
- }
-
- // check if any key is pressed
- if (keys['KeyW'] || keys['KeyS'] || keys['KeyA'] || keys['KeyD']) {
- moving = true;
- } else {
- moving = false;
- }
-
- // Play walking animation if moving
- if (mixer) {
- if (walkAction && idleAction) {
- walkAction.enabled = moving;
- idleAction.enabled = !moving;
- }
- }
- }
-
- renderer.render(scene, camera);
-}
-
-const threejs = import.meta.env.VITE_THREE_JS === "true";
-if (threejs) {
- init();
- animate();
-}
diff --git a/painter-js/src/constants.js b/painter-js/src/constants.js
deleted file mode 100644
index 5f5b670..0000000
--- a/painter-js/src/constants.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const isDebug = import.meta.env.VITE_IS_DEBUG === "true";
-
-export const pixelSize = 10;
-export const pixelCooldown = 10 * 1000; // 10 seconds
-export const canvasEndpoint = isDebug
- ? "http://localhost:3000/canvas/"
- : "/canvas/";
-export const checkEndpoint = isDebug
- ? "http://localhost:3000/check/"
- : "/check/";
-export const WIDTH = 500;
-export const HEIGHT = 500;
diff --git a/painter-js/src/counter.js b/painter-js/src/counter.js
deleted file mode 100644
index 16c3674..0000000
--- a/painter-js/src/counter.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import { pixelCooldown } from "./constants.js";
-
-const countdownDiv = document.getElementById("countdown");
-
-export const updateCountdown = () => {
- setInterval(() => {
- const lastPixelTime = parseInt(
- localStorage.getItem("lastPixelTime") || "0"
- );
- const now = Date.now();
- const timeLeft = Math.max(0, pixelCooldown - (now - lastPixelTime));
- if (timeLeft > 0) {
- countdownDiv.textContent = `You can place a pixel in ${Math.ceil(
- timeLeft / 1000
- )} seconds`;
- } else {
- countdownDiv.textContent = "You can place a pixel now";
- }
- }, 1000);
-};
diff --git a/painter-js/src/domain/canvas-state.js b/painter-js/src/domain/canvas-state.js
new file mode 100644
index 0000000..1d40671
--- /dev/null
+++ b/painter-js/src/domain/canvas-state.js
@@ -0,0 +1,11 @@
+import { WIDTH } from "./constants.js";
+
+export const getPixelIndex = (x, y) => y * WIDTH + x;
+
+export const setPixel = (state, x, y, color) => {
+ state[getPixelIndex(x, y)] = color;
+};
+
+export const getPixel = (state, x, y) => {
+ return state[getPixelIndex(x, y)];
+};
diff --git a/painter-js/src/utils.js b/painter-js/src/domain/color.js
similarity index 66%
rename from painter-js/src/utils.js
rename to painter-js/src/domain/color.js
index e7dac6c..6cf79cc 100644
--- a/painter-js/src/utils.js
+++ b/painter-js/src/domain/color.js
@@ -6,13 +6,16 @@ export const hexToU32 = (color) => {
return parseInt(color.slice(1), 16);
};
-export const getColorFromElementCSS = (element) => {
- return window.getComputedStyle(element).backgroundColor;
-};
-
export const rgbToHex = (rgbProperty) => {
const rgb = rgbProperty.match(/\d+/g);
return `#${rgb
.map((x) => parseInt(x).toString(16).padStart(2, "0"))
.join("")}`;
};
+
+export const u32ToRGBA = (color, alpha) => {
+ const r = (color >> 16) & 0xff;
+ const g = (color >> 8) & 0xff;
+ const b = color & 0xff;
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
+};
diff --git a/painter-js/src/domain/constants.js b/painter-js/src/domain/constants.js
new file mode 100644
index 0000000..73c8a20
--- /dev/null
+++ b/painter-js/src/domain/constants.js
@@ -0,0 +1,4 @@
+export const PIXEL_SIZE = 1;
+export const PIXEL_COOLDOWN = 10 * 1000;
+export const WIDTH = 500;
+export const HEIGHT = 500;
diff --git a/painter-js/src/domain/cooldown.js b/painter-js/src/domain/cooldown.js
new file mode 100644
index 0000000..a94269b
--- /dev/null
+++ b/painter-js/src/domain/cooldown.js
@@ -0,0 +1,9 @@
+import { PIXEL_COOLDOWN } from "./constants.js";
+
+export const canPlacePixel = (lastPlacementTime) => {
+ return Date.now() - lastPlacementTime >= PIXEL_COOLDOWN;
+};
+
+export const timeRemaining = (lastPlacementTime) => {
+ return Math.max(0, PIXEL_COOLDOWN - (Date.now() - lastPlacementTime));
+};
diff --git a/painter-js/src/domain/coords.js b/painter-js/src/domain/coords.js
new file mode 100644
index 0000000..cbbee59
--- /dev/null
+++ b/painter-js/src/domain/coords.js
@@ -0,0 +1,19 @@
+import { WIDTH, HEIGHT } from "./constants.js";
+
+export const getCanvasCoords = (event, canvas) => {
+ const rect = canvas.getBoundingClientRect();
+ const clientX = event.touches ? event.touches[0].clientX : event.clientX;
+ const clientY = event.touches ? event.touches[0].clientY : event.clientY;
+ const scaleX = canvas.width / rect.width;
+ const scaleY = canvas.height / rect.height;
+ return {
+ x: Math.min(
+ Math.max(Math.floor((clientX - rect.left) * scaleX), 0),
+ WIDTH - 1,
+ ),
+ y: Math.min(
+ Math.max(Math.floor((clientY - rect.top) * scaleY), 0),
+ HEIGHT - 1,
+ ),
+ };
+};
diff --git a/painter-js/src/infrastructure/api.js b/painter-js/src/infrastructure/api.js
new file mode 100644
index 0000000..0784b7c
--- /dev/null
+++ b/painter-js/src/infrastructure/api.js
@@ -0,0 +1,5 @@
+const isDebug = import.meta.env.VITE_IS_DEBUG === "true";
+
+const CHECK_ENDPOINT = isDebug ? "http://localhost:3000/check/" : "/check/";
+
+export const checkServer = () => fetch(CHECK_ENDPOINT);
diff --git a/painter-js/src/infrastructure/socket-client.js b/painter-js/src/infrastructure/socket-client.js
new file mode 100644
index 0000000..08b671a
--- /dev/null
+++ b/painter-js/src/infrastructure/socket-client.js
@@ -0,0 +1,72 @@
+import io from "socket.io-client";
+
+const isDebug = import.meta.env.VITE_IS_DEBUG === "true";
+const transport = import.meta.env.VITE_TRANSPORT || "socketio";
+
+const createSocketIoTransport = () => {
+ const url = isDebug ? "ws://localhost:3000" : undefined;
+ const socket = url ? io(url) : io({ transports: ["websocket"] });
+
+ return {
+ on: (event, handler) => socket.on(event, handler),
+ emit: (event, data) => socket.emit(event, data),
+ };
+};
+
+const createWebSocketTransport = () => {
+ const handlers = {};
+ const wsUrl = isDebug
+ ? "ws://localhost:3000/ws"
+ : `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/ws`;
+
+ const ws = new WebSocket(wsUrl);
+ ws.binaryType = "arraybuffer";
+
+ const on = (event, handler) => {
+ if (!handlers[event]) handlers[event] = [];
+ handlers[event].push(handler);
+ };
+
+ const emit = (event, data) => {
+ if (ws.readyState !== WebSocket.OPEN) return;
+ ws.send(JSON.stringify({ type: event, ...data }));
+ };
+
+ const dispatch = (event, ...args) => {
+ (handlers[event] || []).forEach((handler) => handler(...args));
+ };
+
+ ws.addEventListener("open", () => dispatch("connect"));
+
+ ws.addEventListener("message", (event) => {
+ if (event.data instanceof ArrayBuffer) {
+ const pixels = new Uint32Array(event.data);
+ dispatch("canvas_state", Array.from(pixels));
+ return;
+ }
+
+ const message = JSON.parse(event.data);
+ switch (message.type) {
+ case "pixel-updated":
+ dispatch("pixel-updated", message);
+ break;
+ case "current_soldiers":
+ dispatch("current_soldiers", message.count);
+ break;
+ case "error":
+ dispatch("error", message.message);
+ break;
+ }
+ });
+
+ ws.addEventListener("close", () => dispatch("disconnect"));
+
+ return { on, emit };
+};
+
+export const createSocketConnection = () => {
+ if (transport === "websocket") {
+ return createWebSocketTransport();
+ }
+ return createSocketIoTransport();
+};
diff --git a/painter-js/src/main.js b/painter-js/src/main.js
index 99c1b20..ed544af 100644
--- a/painter-js/src/main.js
+++ b/painter-js/src/main.js
@@ -1,58 +1,88 @@
-import { connectToWS } from "./socket.js";
-import "./canvas.js";
-import "./counter.js";
-import { updateCountdown } from "./counter.js";
-import { checkEndpoint, pixelSize } from "./constants.js";
-import { handleSocketEvents } from "./canvas.js";
-import "./challenge.js"
+import { createSocketConnection } from "./infrastructure/socket-client.js";
+import { checkServer } from "./infrastructure/api.js";
+import { createCanvasRenderer } from "./ui/canvas-renderer.js";
+import { createColorPalette } from "./ui/color-palette.js";
+import { createPixelPlacer } from "./ui/pixel-placer.js";
+import { startCooldownDisplay } from "./ui/cooldown-display.js";
+import { createCanvasViewport } from "./ui/canvas-viewport.js";
+import { setPixel } from "./domain/canvas-state.js";
+import { getCanvasCoords } from "./domain/coords.js";
-const isDebug = import.meta.env.VITE_IS_DEBUG === "true";
-
-const currentSoldiersSpan = document.getElementById("current-soldiers");
-
-let coords = [];
-const canvas = document.getElementById("canvas");
+const canvasEl = document.getElementById("canvas");
const coordsText = document.getElementById("coords");
-const ogCanvasStyle = canvas.style.display;
-canvas.style.display = "none";
+const currentSoldiersSpan = document.getElementById("current-soldiers");
+const statusEl = document.getElementById("connection-status");
-fetch(checkEndpoint)
+const savedDisplay = canvasEl.style.display;
+canvasEl.style.display = "none";
+
+let canvasState = [];
+
+const renderer = createCanvasRenderer(canvasEl);
+const palette = createColorPalette();
+
+startCooldownDisplay();
+createCanvasViewport(canvasEl);
+
+canvasEl.addEventListener("mousemove", (event) => {
+ const { x, y } = getCanvasCoords(event, canvasEl);
+ coordsText.textContent = `${x}, ${y}`;
+});
+
+document.getElementById("save-canvas").addEventListener("click", () => {
+ const a = document.createElement("a");
+ a.href = renderer.toDataURL();
+ a.download = "canvas.png";
+ a.click();
+});
+
+const showStatus = (message, isError) => {
+ if (!statusEl) return;
+ statusEl.textContent = message;
+ statusEl.className = isError
+ ? "text-red-500 text-sm"
+ : "text-green-500 text-sm";
+};
+
+checkServer()
.then((response) => {
- if (response.ok) {
- const socket = connectToWS();
+ if (!response.ok) throw new Error("Server unavailable");
- socket.on("connect", () => {
- canvas.style.display = ogCanvasStyle;
- console.log("connect");
- });
+ const socket = createSocketConnection();
- socket.on("error", (message) => {
- alert(message);
- });
+ socket.on("connect", () => {
+ canvasEl.style.display = savedDisplay;
+ showStatus("Connected", false);
+ });
- socket.on("current_soldiers", (currentSoldiers) => {
- currentSoldiersSpan.textContent = currentSoldiers;
- });
+ socket.on("canvas_state", (data) => {
+ canvasState = data;
+ renderer.drawState(data);
+ });
- handleSocketEvents(socket);
+ socket.on("error", (message) => showStatus(message, true));
- requestAnimationFrame(updateCountdown);
+ socket.on("current_soldiers", (count) => {
+ currentSoldiersSpan.textContent = count;
+ });
- window.addEventListener("mousemove", (event) => {
- // get coordinates of the mouse inside the canvas
- const rect = canvas.getBoundingClientRect();
- const x = Math.floor((event.clientX - rect.left) / pixelSize);
- const y = Math.floor((event.clientY - rect.top) / pixelSize);
- coords = [x, y];
+ socket.on("pixel-updated", (update) => {
+ renderer.drawPixel(update.x, update.y, update.color);
+ setPixel(canvasState, update.x, update.y, update.color);
+ });
- coordsText.textContent = `${x}, ${y}`;
- });
- } else {
- throw new Error("Can't connect to the server");
- }
+ socket.on("disconnect", () => {
+ showStatus("Disconnected — reconnecting...", true);
+ });
+
+ createPixelPlacer({
+ canvas: canvasEl,
+ renderer,
+ getColor: palette.getColor,
+ getState: () => canvasState,
+ socket,
+ });
})
- .catch((error) => {
- alert(
- "You have already connected to the server from another tab or window. Please close the other tab or window and refresh this page."
- );
+ .catch(() => {
+ showStatus("Cannot connect to server", true);
});
diff --git a/painter-js/src/socket.js b/painter-js/src/socket.js
deleted file mode 100644
index 5774edb..0000000
--- a/painter-js/src/socket.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import io from "socket.io-client";
-
-const isDebug = import.meta.env.VITE_IS_DEBUG === "true";
-const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
-const wsHost = window.location.host;
-
-let socket;
-
-export const connectToWS = () => {
- if (isDebug) {
- socket = io("ws://localhost:3000");
- } else {
- socket = io(`${wsProtocol}//${wsHost}`, {
- transports: ["websocket"],
- });
- }
-
- return socket;
-};
-
-export default socket;
diff --git a/painter-js/src/style.css b/painter-js/src/style.css
index 15550e5..abd3730 100644
--- a/painter-js/src/style.css
+++ b/painter-js/src/style.css
@@ -2,20 +2,17 @@
@tailwind components;
@tailwind utilities;
-@layer base {
- .rainbow-border {
- position: relative;
- display: inline-block;
- background: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet);
+.canvas-viewport {
+ overflow: auto;
+ width: min(92vw, 70vh);
+ height: min(92vw, 70vh);
+ border: 2px solid #94a3b8;
+ border-radius: 4px;
+ cursor: crosshair;
}
-.rainbow-border::before {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- background: white;
- z-index: -1; /* Place behind the content */
+
+#canvas {
+ image-rendering: pixelated;
+ image-rendering: crisp-edges;
+ display: block;
}
-}
\ No newline at end of file
diff --git a/painter-js/src/ui/canvas-renderer.js b/painter-js/src/ui/canvas-renderer.js
new file mode 100644
index 0000000..e8af1a8
--- /dev/null
+++ b/painter-js/src/ui/canvas-renderer.js
@@ -0,0 +1,39 @@
+import { u32ToHex, u32ToRGBA } from "../domain/color.js";
+import { WIDTH, HEIGHT } from "../domain/constants.js";
+
+export const createCanvasRenderer = (canvas) => {
+ const ctx = canvas.getContext("2d");
+
+ const drawState = (state) => {
+ const imageData = ctx.createImageData(WIDTH, HEIGHT);
+ const data = imageData.data;
+ for (let i = 0; i < state.length; i++) {
+ const color = state[i];
+ const offset = i * 4;
+ data[offset] = (color >> 16) & 0xff;
+ data[offset + 1] = (color >> 8) & 0xff;
+ data[offset + 2] = color & 0xff;
+ data[offset + 3] = 255;
+ }
+ ctx.putImageData(imageData, 0, 0);
+ };
+
+ const drawPixel = (x, y, colorU32) => {
+ ctx.fillStyle = u32ToHex(colorU32);
+ ctx.fillRect(x, y, 1, 1);
+ };
+
+ const drawPreview = (x, y, colorU32) => {
+ ctx.fillStyle = u32ToRGBA(colorU32, 0.5);
+ ctx.fillRect(x, y, 1, 1);
+ };
+
+ const restorePixel = (state, x, y) => {
+ ctx.fillStyle = u32ToHex(state[y * WIDTH + x]);
+ ctx.fillRect(x, y, 1, 1);
+ };
+
+ const toDataURL = () => canvas.toDataURL();
+
+ return { drawState, drawPixel, drawPreview, restorePixel, toDataURL };
+};
diff --git a/painter-js/src/ui/canvas-viewport.js b/painter-js/src/ui/canvas-viewport.js
new file mode 100644
index 0000000..fa8fc18
--- /dev/null
+++ b/painter-js/src/ui/canvas-viewport.js
@@ -0,0 +1,78 @@
+const MIN_ZOOM = 1;
+const MAX_ZOOM = 40;
+
+export const createCanvasViewport = (canvas) => {
+ const viewport = canvas.parentElement;
+ const zoomLabel = document.getElementById("zoom-level");
+ let zoom = 1;
+ let baseSize = viewport.clientWidth;
+
+ const applyZoom = () => {
+ const size = baseSize * zoom;
+ canvas.style.width = `${size}px`;
+ canvas.style.height = `${size}px`;
+ if (zoomLabel) zoomLabel.textContent = `${zoom.toFixed(1)}x`;
+ };
+
+ const setZoom = (newZoom, centerX, centerY) => {
+ const oldZoom = zoom;
+ zoom = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, newZoom));
+ if (zoom === oldZoom) return;
+
+ if (centerX !== undefined && centerY !== undefined) {
+ const ratio = zoom / oldZoom;
+ viewport.scrollLeft = (viewport.scrollLeft + centerX) * ratio - centerX;
+ viewport.scrollTop = (viewport.scrollTop + centerY) * ratio - centerY;
+ }
+
+ applyZoom();
+ };
+
+ viewport.addEventListener("wheel", (event) => {
+ event.preventDefault();
+ const factor = event.deltaY > 0 ? 0.8 : 1.25;
+ const rect = viewport.getBoundingClientRect();
+ setZoom(zoom * factor, event.clientX - rect.left, event.clientY - rect.top);
+ });
+
+ let lastPinchDist = 0;
+ viewport.addEventListener("touchmove", (event) => {
+ if (event.touches.length !== 2) return;
+ event.preventDefault();
+ const dist = Math.hypot(
+ event.touches[0].clientX - event.touches[1].clientX,
+ event.touches[0].clientY - event.touches[1].clientY,
+ );
+ if (lastPinchDist > 0) {
+ const midX =
+ (event.touches[0].clientX + event.touches[1].clientX) / 2 -
+ viewport.getBoundingClientRect().left;
+ const midY =
+ (event.touches[0].clientY + event.touches[1].clientY) / 2 -
+ viewport.getBoundingClientRect().top;
+ setZoom(zoom * (dist / lastPinchDist), midX, midY);
+ }
+ lastPinchDist = dist;
+ });
+
+ viewport.addEventListener("touchend", () => {
+ lastPinchDist = 0;
+ });
+
+ document.getElementById("zoom-in")?.addEventListener("click", () => {
+ setZoom(zoom * 1.5);
+ applyZoom();
+ });
+
+ document.getElementById("zoom-out")?.addEventListener("click", () => {
+ setZoom(zoom / 1.5);
+ applyZoom();
+ });
+
+ window.addEventListener("resize", () => {
+ baseSize = viewport.clientWidth;
+ applyZoom();
+ });
+
+ applyZoom();
+};
diff --git a/painter-js/src/ui/color-palette.js b/painter-js/src/ui/color-palette.js
new file mode 100644
index 0000000..5234a11
--- /dev/null
+++ b/painter-js/src/ui/color-palette.js
@@ -0,0 +1,53 @@
+import { rgbToHex } from "../domain/color.js";
+
+const PALETTE_IDS = [
+ "red",
+ "green",
+ "blue",
+ "yellow",
+ "purple",
+ "pink",
+ "cyan",
+ "white",
+ "black",
+ "orange",
+ "brown",
+];
+
+export const createColorPalette = () => {
+ const colorPicker = document.getElementById("color-picker");
+ const currentColorSpan = document.getElementById("current-color-span");
+ let activeButton = null;
+
+ let currentColor = localStorage.getItem("currentColor") || "#000000";
+ colorPicker.value = currentColor;
+ currentColorSpan.style.backgroundColor = currentColor;
+
+ const setColor = (hex, button) => {
+ currentColor = hex;
+ currentColorSpan.textContent = hex;
+ currentColorSpan.style.backgroundColor = hex;
+ localStorage.setItem("currentColor", hex);
+
+ if (activeButton)
+ activeButton.classList.remove("ring-2", "ring-offset-2", "ring-cyan-400");
+ if (button) {
+ button.classList.add("ring-2", "ring-offset-2", "ring-cyan-400");
+ activeButton = button;
+ }
+ };
+
+ colorPicker.addEventListener("input", (e) => setColor(e.target.value, null));
+
+ for (const id of PALETTE_IDS) {
+ const button = document.getElementById(id);
+ button.addEventListener("click", () => {
+ setColor(
+ rgbToHex(window.getComputedStyle(button).backgroundColor),
+ button,
+ );
+ });
+ }
+
+ return { getColor: () => currentColor };
+};
diff --git a/painter-js/src/ui/cooldown-display.js b/painter-js/src/ui/cooldown-display.js
new file mode 100644
index 0000000..eb99b52
--- /dev/null
+++ b/painter-js/src/ui/cooldown-display.js
@@ -0,0 +1,16 @@
+import { timeRemaining } from "../domain/cooldown.js";
+
+export const startCooldownDisplay = () => {
+ const countdownDiv = document.getElementById("countdown");
+
+ setInterval(() => {
+ const lastPlacementTime = parseInt(
+ localStorage.getItem("lastPixelTime") || "0",
+ );
+ const remaining = timeRemaining(lastPlacementTime);
+ countdownDiv.textContent =
+ remaining > 0
+ ? `You can place a pixel in ${Math.ceil(remaining / 1000)} seconds`
+ : "You can place a pixel now";
+ }, 1000);
+};
diff --git a/painter-js/src/ui/pixel-placer.js b/painter-js/src/ui/pixel-placer.js
new file mode 100644
index 0000000..2127eed
--- /dev/null
+++ b/painter-js/src/ui/pixel-placer.js
@@ -0,0 +1,57 @@
+import { hexToU32 } from "../domain/color.js";
+import { canPlacePixel } from "../domain/cooldown.js";
+import { setPixel } from "../domain/canvas-state.js";
+import { getCanvasCoords } from "../domain/coords.js";
+
+export const createPixelPlacer = ({
+ canvas,
+ renderer,
+ getColor,
+ getState,
+ socket,
+}) => {
+ let previewPixel = null;
+
+ const place = (update) => {
+ const lastPixelTime = parseInt(
+ localStorage.getItem("lastPixelTime") || "0",
+ );
+ if (!canPlacePixel(lastPixelTime)) return;
+
+ socket.emit("place-pixel", update);
+ setPixel(getState(), update.x, update.y, update.color);
+ localStorage.setItem("lastPixelTime", Date.now().toString());
+ };
+
+ const clearPreview = () => {
+ if (!previewPixel) return;
+ renderer.restorePixel(getState(), previewPixel.x, previewPixel.y);
+ previewPixel = null;
+ };
+
+ const confirmPlacement = () => {
+ if (!previewPixel) return;
+ place(previewPixel);
+ clearPreview();
+ };
+
+ const selectPixel = (event) => {
+ const { x, y } = getCanvasCoords(event, canvas);
+ const color = hexToU32(getColor());
+
+ clearPreview();
+ previewPixel = { x, y, color };
+ renderer.drawPreview(x, y, color);
+ };
+
+ canvas.addEventListener("click", selectPixel);
+
+ window.addEventListener("keydown", (event) => {
+ if (event.key === "Enter") confirmPlacement();
+ if (event.key === "Escape") clearPreview();
+ });
+
+ document
+ .getElementById("place-pixel")
+ .addEventListener("click", confirmPlacement);
+};
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 2da7653..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,251 +0,0 @@
-use std::{
- collections::{HashMap, HashSet},
- net::SocketAddr,
- sync::{Arc, Mutex},
-};
-
-use axum::{routing::get, Extension, Json, Router};
-use chrono::{DateTime, Duration, Utc};
-use memory_stats::memory_stats;
-use serde::{Deserialize, Serialize};
-use serde_json::Value;
-use socketioxide::{
- extract::{Bin, Data, SocketRef},
- SocketIo,
-};
-use tower_governor::{governor::GovernorConfigBuilder, GovernorLayer};
-use tower_http::{
- cors::{Any, CorsLayer},
- services::ServeDir,
-};
-use tracing::info;
-use tracing_subscriber::FmtSubscriber;
-
-type Canvas = Arc
>>;
-type LastUpdate = Arc>>>;
-type Soldiers = Arc>>;
-
-#[derive(Serialize, Deserialize, Debug)]
-struct PixelUpdate {
- x: u32,
- y: u32,
- color: u32,
-}
-
-async fn on_connect(
- socket: SocketRef,
- canvas: Canvas,
- last_update: LastUpdate,
- soldiers: Soldiers,
-) {
- info!("Socket connected: {:?} {:?}", socket.ns(), socket.id,);
-
- let socket_id = socket.id.to_string();
-
- {
- let mut soldiers = soldiers.lock().unwrap();
- soldiers.insert(socket_id.clone());
- let soldiers_num = soldiers.len();
-
- socket.emit("current_soldiers", &soldiers_num).ok();
- socket
- .broadcast()
- .emit("current_soldiers", &soldiers_num)
- .ok();
- }
-
- info!("Memory usage after connection and before cloning state: ");
- print_memory_usage();
-
- let canvas_clone = canvas.clone();
- let last_update_clone = last_update.clone();
-
- info!("Memory usage after connection and after cloning state: ");
- print_memory_usage();
-
- socket.on(
- "place-pixel",
- move |socket: SocketRef, Data::(update), Bin(_bin)| {
- let socket_id = socket.id.to_string();
- let last_update = last_update_clone.clone();
- let canvas = canvas_clone.clone();
-
- {
- let now = Utc::now();
- let mut last_update = last_update.lock().unwrap();
-
- if let Some(&last_time) = last_update.get(&socket_id) {
- if now < last_time + Duration::seconds(10) {
- let _ = socket.emit(
- "error",
- Value::String("You can only place one pixel per minute".to_string()),
- );
- return;
- }
- }
-
- last_update.insert(socket_id.clone(), now);
- }
-
- info!("Received pixel update: {:?}", update);
- let mut canvas = canvas.lock().unwrap();
- canvas[update.y as usize * 500 + update.x as usize] = update.color;
-
- info!("Emitting pixel update");
- socket.emit("pixel-updated", &update).ok(); // Send to the user who placed the pixel
- socket.broadcast().emit("pixel-updated", &update).ok(); // Send to all other users
-
- info!("Memory usage after pixel update: ");
- print_memory_usage();
- },
- );
-
- socket.on_disconnect(move |socket: SocketRef| {
- info!("Socket disconnected: {:?}", socket.id);
- info!("Memory usage after disconnection: ");
- print_memory_usage();
- let socket_id = socket.id.to_string();
-
- {
- let mut soldiers = soldiers.lock().unwrap();
- soldiers.remove(&socket_id.to_string());
- info!("Soldiers: {:?}", soldiers.len());
-
- let soldiers_num = soldiers.len();
- socket
- .broadcast()
- .emit("current_soldiers", &soldiers_num)
- .ok();
- }
-
- {
- let mut last_update = last_update.lock().unwrap();
- last_update.remove(&socket_id);
-
- info!("Last update: {:?}", last_update.len());
- }
-
- info!("Memory usage after disconnection and cleanup: ");
- print_memory_usage();
- });
-}
-
-fn print_memory_usage() {
- if let Some(usage) = memory_stats() {
- info!(
- "Current physical memory usage: {} MB",
- usage.physical_mem as f64 / 1024.0 / 1024.0
- );
- info!(
- "Current virtual memory usage: {} MB",
- usage.virtual_mem as f64 / 1024.0 / 1024.0
- );
- }
-}
-
-async fn get_canvas_state(Extension(canvas): Extension