diff --git a/bun.lock b/bun.lock index c3fa714..7eeaed1 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,9 @@ "name": "loreal-game", "dependencies": { "@tailwindcss/vite": "^4.1.11", + "@types/express": "^4.17.21", + "@types/node": "^24.0.13", + "express": "^4.19.2", "gsap": "^3.13.0", "jszip": "^3.10.1", "normalize.css": "^8.0.1", @@ -175,8 +178,30 @@ "@tailwindcss/vite": ["@tailwindcss/vite@4.1.11", "", { "dependencies": { "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "tailwindcss": "4.1.11" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw=="], + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], + + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/express": ["@types/express@4.17.23", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ=="], + + "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.6", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A=="], + + "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], + + "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + + "@types/node": ["@types/node@24.0.13", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ=="], + + "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], + + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + + "@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="], + + "@types/serve-static": ["@types/serve-static@1.15.8", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "*" } }, "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg=="], + "@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.0", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-beta.19" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", "vue": "^3.2.25" } }, "sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ=="], "@volar/language-core": ["@volar/language-core@2.4.15", "", { "dependencies": { "@volar/source-map": "2.4.15" } }, "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA=="], @@ -209,6 +234,8 @@ "@vue/tsconfig": ["@vue/tsconfig@0.7.0", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg=="], + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + "alien-signals": ["alien-signals@1.0.13", "", {}, "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg=="], "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], @@ -217,6 +244,8 @@ "ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="], "b4a": ["b4a@1.6.7", "", {}, "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg=="], @@ -237,6 +266,8 @@ "bl": ["bl@5.1.0", "", { "dependencies": { "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ=="], + "body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g=="], + "boxen": ["boxen@7.1.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^7.0.1", "chalk": "^5.2.0", "cli-boxes": "^3.0.0", "string-width": "^5.1.2", "type-fest": "^2.13.0", "widest-line": "^4.0.1", "wrap-ansi": "^8.1.0" } }, "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog=="], "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], @@ -247,6 +278,12 @@ "buffer-builder": ["buffer-builder@0.2.0", "", {}, "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "camelcase": ["camelcase@7.0.1", "", {}, "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw=="], "caniuse-lite": ["caniuse-lite@1.0.30001726", "", {}, "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw=="], @@ -271,6 +308,14 @@ "colorjs.io": ["colorjs.io@0.5.2", "", {}, "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw=="], + "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="], + + "cookie-signature": ["cookie-signature@1.0.6", "", {}, "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="], + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -279,58 +324,104 @@ "de-indent": ["de-indent@1.0.2", "", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="], + "debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], + "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "electron-to-chromium": ["electron-to-chromium@1.5.179", "", {}, "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ=="], "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], "enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="], "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + "express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="], + "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], "fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="], "filesize": ["filesize@10.1.6", "", {}, "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w=="], + "finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], + "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "gsap": ["gsap@3.13.0", "", {}, "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="], + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + + "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="], @@ -341,6 +432,8 @@ "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], @@ -391,6 +484,20 @@ "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + + "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], + + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], + + "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], @@ -407,12 +514,16 @@ "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + "ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], + "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + "node-abi": ["node-abi@3.75.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg=="], "node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="], @@ -423,6 +534,10 @@ "normalize.css": ["normalize.css@8.0.1", "", {}, "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], @@ -433,12 +548,16 @@ "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], @@ -451,8 +570,16 @@ "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], + "qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="], + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], @@ -463,7 +590,9 @@ "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], - "safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "sass-embedded": ["sass-embedded@1.89.2", "", { "dependencies": { "@bufbuild/protobuf": "^2.5.0", "buffer-builder": "^0.2.0", "colorjs.io": "^0.5.0", "immutable": "^5.0.2", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", "varint": "^6.0.0" }, "optionalDependencies": { "sass-embedded-android-arm": "1.89.2", "sass-embedded-android-arm64": "1.89.2", "sass-embedded-android-riscv64": "1.89.2", "sass-embedded-android-x64": "1.89.2", "sass-embedded-darwin-arm64": "1.89.2", "sass-embedded-darwin-x64": "1.89.2", "sass-embedded-linux-arm": "1.89.2", "sass-embedded-linux-arm64": "1.89.2", "sass-embedded-linux-musl-arm": "1.89.2", "sass-embedded-linux-musl-arm64": "1.89.2", "sass-embedded-linux-musl-riscv64": "1.89.2", "sass-embedded-linux-musl-x64": "1.89.2", "sass-embedded-linux-riscv64": "1.89.2", "sass-embedded-linux-x64": "1.89.2", "sass-embedded-win32-arm64": "1.89.2", "sass-embedded-win32-x64": "1.89.2" }, "bin": { "sass": "dist/bin/sass.js" } }, "sha512-Ack2K8rc57kCFcYlf3HXpZEJFNUX8xd8DILldksREmYXQkRHI879yy8q4mRDJgrojkySMZqmmmW1NxrFxMsYaA=="], @@ -501,14 +630,28 @@ "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="], + + "serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="], + "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + "sharp": ["sharp@0.32.6", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.2", "node-addon-api": "^6.1.0", "prebuild-install": "^7.1.1", "semver": "^7.5.4", "simple-get": "^4.0.1", "tar-fs": "^3.0.4", "tunnel-agent": "^0.6.0" } }, "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], @@ -519,6 +662,8 @@ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + "stdin-discarder": ["stdin-discarder@0.1.0", "", { "dependencies": { "bl": "^5.0.0" } }, "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ=="], "streamx": ["streamx@2.22.1", "", { "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" } }, "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA=="], @@ -555,20 +700,32 @@ "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], "type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], + "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], + "varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "vite": ["vite@7.0.2", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-hxdyZDY1CM6SNpKI4w4lcUc3Mtkd9ej4ECWVHSMrOdSinVc2zYOAppHeGc/hzmRo3pxM5blMzkuWHOJA/3NiFw=="], "vite-plugin-minipic": ["vite-plugin-minipic@1.3.0", "", { "dependencies": { "@rollup/pluginutils": "^5.0.3", "boxen": "^7.1.1", "chalk": "^5.3.0", "filesize": "^10.0.12", "glob": "^10.3.10", "lodash-es": "^4.17.21", "ora": "^7.0.1", "rollup": "^4.40.0", "sharp": "^0.32.4" } }, "sha512-iSfqsXqxDg0NZW+3UrSG9PkLjL2DbleDoXeI5jd48kze770bOuJa4WhH4/OYhaDvy8jaCTSVeV5jmDksIcXh+w=="], @@ -611,14 +768,24 @@ "prebuild-install/tar-fs": ["tar-fs@2.1.3", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg=="], + "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], + + "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "tunnel-agent/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], diff --git a/index.html b/index.html index b170ff4..bf03bf7 100644 --- a/index.html +++ b/index.html @@ -4,9 +4,19 @@ - - + + 弹性福利平台 + diff --git a/package.json b/package.json index 0725a5a..262da07 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,16 @@ "scripts": { "dev": "vite", "build": "vue-tsc -b && vite build", - "preview": "vite preview" + "preview": "vite preview", + "server": "bun run server.ts", + "server:dev": "bun --watch server.ts", + "start": "vue-tsc -b && vite build && bun run server.ts" }, "dependencies": { "@tailwindcss/vite": "^4.1.11", + "@types/express": "^4.17.21", + "@types/node": "^24.0.13", + "express": "^4.19.2", "gsap": "^3.13.0", "jszip": "^3.10.1", "normalize.css": "^8.0.1", diff --git a/examples/usage-examples.ts b/pm2.config.js similarity index 100% rename from examples/usage-examples.ts rename to pm2.config.js diff --git a/scores.json b/scores.json new file mode 100644 index 0000000..1c8ef09 --- /dev/null +++ b/scores.json @@ -0,0 +1,51 @@ +[ + { + "name": "Mike", + "time": 45.5, + "region": "大北区", + "store": "大连恒隆广场", + "timestamp": 1752309222243 + }, + { + "name": "小明", + "time": 98.2, + "region": "大西区", + "store": "太原万象城", + "timestamp": 1752309242794 + }, + { + "name": "小白", + "time": 83.2, + "region": "大西区", + "store": "太原万象城", + "timestamp": 1752309242794 + }, + { + "name": "兔宝", + "time": 138.2, + "region": "奥莱东区", + "store": "广州太古汇", + "timestamp": 1752309242794 + }, + { + "name": "Elena", + "time": 122.2, + "region": "大南区", + "store": "深圳湾万象城", + "timestamp": 1752309242794 + }, + { + "name": "Tom", + "time": 133.2, + "region": "免税", + "store": "海口新海港", + "timestamp": 1752309242794 + }, + { + "name": "阿迪斯", + "time": 1.9645, + "region": "奥莱", + "store": "北京斯普瑞斯", + "timestamp": 1752309530750 + } +] \ No newline at end of file diff --git a/server.ts b/server.ts new file mode 100644 index 0000000..4a552ed --- /dev/null +++ b/server.ts @@ -0,0 +1,177 @@ +import express from 'express'; +import { promises as fs } from 'fs'; +import path from 'path'; + +const app = express(); +const PORT = process.env.PORT || 3001; +const SCORES_FILE = path.join(__dirname, 'scores.json'); + +// 分数数据类型 +interface ScoreEntry { + name: string; + time: number; + region: string; + store: string; + timestamp: number; +} + +// 中间件 +app.use(express.json()); +app.use(express.static('dist')); // 服务静态文件 + +// 初始化分数文件 +async function initScoresFile() { + try { + await fs.access(SCORES_FILE); + } catch { + // 文件不存在,创建空数组 + await fs.writeFile(SCORES_FILE, JSON.stringify([])); + } +} + +// 读取分数数据 +async function readScores(): Promise { + try { + const data = await fs.readFile(SCORES_FILE, 'utf-8'); + return JSON.parse(data); + } catch { + return []; + } +} + +// 写入分数数据 +async function writeScores(scores: ScoreEntry[]): Promise { + await fs.writeFile(SCORES_FILE, JSON.stringify(scores, null, 2)); +} + +// POST /api/score - 提交分数 +app.post('/api/score', async (req, res) => { + try { + const { region, store, name, time } = req.body; + + // 验证必要字段 + if (!region || !store || !name || typeof time !== 'number') { + return res.status(400).json({ + error: '缺少必要字段: region, store, name, time' + }); + } + + // 验证时间值 + if (time <= 0) { + return res.status(400).json({ + error: '时间必须大于0' + }); + } + + const scores = await readScores(); + + // 创建新的分数记录 + const newScore: ScoreEntry = { + name: String(name).trim(), + time: Number(time), + region: String(region).trim(), + store: String(store).trim(), + timestamp: Date.now() + }; + + // 检查是否已有相同用户的记录 + const existingIndex = scores.findIndex( + score => score.name === newScore.name && + score.region === newScore.region && + score.store === newScore.store + ); + + if (existingIndex !== -1) { + // 如果新时间更好(更短),则更新记录 + if (newScore.time < scores[existingIndex].time) { + scores[existingIndex] = newScore; + } + } else { + // 添加新记录 + scores.push(newScore); + } + + await writeScores(scores); + + res.json({ + success: true, + message: '分数提交成功', + score: newScore + }); + } catch (error) { + console.error('提交分数时出错:', error); + res.status(500).json({ + error: '服务器内部错误' + }); + } +}); + +// GET /api/score - 获取排行榜(前6名) +app.get('/api/score', async (req, res) => { + try { + const scores = await readScores(); + + // 按时间排序(时间短的在前)并取前6名 + const leaderBoard = scores + .sort((a, b) => a.time - b.time) + .slice(0, 6) + .map(score => ({ + name: score.name, + time: score.time, + region: score.region, + store: score.store + })); + + res.json(leaderBoard); + } catch (error) { + console.error('获取排行榜时出错:', error); + res.status(500).json({ + error: '服务器内部错误' + }); + } +}); + +// GET /api/score/all - 获取所有分数(可选,用于管理) +app.get('/api/score/all', async (req, res) => { + try { + const scores = await readScores(); + res.json(scores.sort((a, b) => a.time - b.time)); + } catch (error) { + console.error('获取所有分数时出错:', error); + res.status(500).json({ + error: '服务器内部错误' + }); + } +}); + +// 健康检查端点 +app.get('/api/health', (req, res) => { + res.json({ + status: 'ok', + timestamp: new Date().toISOString() + }); +}); + +// 处理 SPA 路由 - 将所有未匹配的路由返回 index.html +app.get('/*', (req, res) => { + try { + res.sendFile(path.join(__dirname, 'dist', 'index.html')); + } catch (error) { + res.status(404).send('Page not found'); + } +}); + +// 启动服务器 +async function startServer() { + await initScoresFile(); + + app.listen(PORT, () => { + console.log(`🚀 服务器运行在 http://localhost:${PORT}`); + console.log(`📊 API 端点:`); + console.log(` POST /api/score - 提交分数`); + console.log(` GET /api/score - 获取排行榜`); + console.log(` GET /api/health - 健康检查`); + }); +} + +startServer().catch(console.error); \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index cdcd33c..318332c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,8 +1,9 @@ diff --git a/src/assets/bgm.mp3 b/src/assets/bgm.mp3 new file mode 100644 index 0000000..fc9a83a Binary files /dev/null and b/src/assets/bgm.mp3 differ diff --git a/src/assets/index.ts b/src/assets/index.ts index eb893ea..34717b0 100644 --- a/src/assets/index.ts +++ b/src/assets/index.ts @@ -42,5 +42,6 @@ export default { [new URL('./game/metal_0.png', import.meta.url).href,], [new URL('./game/metal_1.png', import.meta.url).href,], [new URL('./game/metal_2.png', import.meta.url).href,] - ] + ], + bgm: new URL('./bgm.mp3', import.meta.url).href, } diff --git a/src/assets/loader/wecare.png b/src/assets/loader/wecare.png new file mode 100644 index 0000000..e0b0bc3 Binary files /dev/null and b/src/assets/loader/wecare.png differ diff --git a/src/assets/p1/请填写你的名字.png b/src/assets/p1/请填写你的名字.png new file mode 100644 index 0000000..6fb9489 Binary files /dev/null and b/src/assets/p1/请填写你的名字.png differ diff --git a/src/assets/关掉音乐.png b/src/assets/关掉音乐.png new file mode 100644 index 0000000..4e0b983 Binary files /dev/null and b/src/assets/关掉音乐.png differ diff --git a/src/assets/开启音乐.png b/src/assets/开启音乐.png new file mode 100644 index 0000000..fd1e5ee Binary files /dev/null and b/src/assets/开启音乐.png differ diff --git a/src/assets/返回按钮.png b/src/assets/返回按钮.png new file mode 100644 index 0000000..f4d1c1b Binary files /dev/null and b/src/assets/返回按钮.png differ diff --git a/src/components/AniEle.vue b/src/components/AniEle.vue index cacafc8..d9c0451 100644 --- a/src/components/AniEle.vue +++ b/src/components/AniEle.vue @@ -35,7 +35,7 @@ const props = withDefaults(defineProps(), { autoPlay: true }) -// 响应式状态 (不变) +// 响应式状态 const loading = ref(false) const error = ref('') const progress = ref(0) @@ -53,6 +53,9 @@ const currentLoopCount = ref(0) const animationId = ref() const lastFrameTime = ref(0) const pendingJumpTo = ref() +// 【新增】: 用于存储 soft jump 的 Promise resolve 函数 +const pendingJumpResolver = ref<((success: boolean) => void) | null>(null) + // loadImageSequence 函数 (不变) const loadImageSequence = async (zipUrl: string) => { @@ -68,7 +71,7 @@ const loadImageSequence = async (zipUrl: string) => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } - 111 + const zipBlob = await response.blob() progress.value = 30 console.log(`加载压缩包: ${zipUrl}, 大小: ${(zipBlob.size / 1024).toFixed(2)} KB`); @@ -144,10 +147,6 @@ const drawFrame = (frameIndex: number) => { if (!canvasRef.value || !loadedImages.value.length) return const safeIndex = Math.max(0, Math.min(frameIndex, loadedImages.value.length - 1)) - if (props.log) { - // console.log(`绘制帧: ${safeIndex}, 总帧数: ${loadedImages.value.length}`); - } - const canvas = canvasRef.value const ctx = canvas.getContext('2d') if (!ctx) return @@ -160,12 +159,17 @@ const drawFrame = (frameIndex: number) => { } // ================================================================= -// 核心修复区域 1: jumpToRule 函数 +// 核心修改区域 1: jumpToRule 函数 // ================================================================= -const jumpToRule = (ruleName: string) => { +const jumpToRule = (ruleName: string): boolean => { const ruleIndex = findRuleIndex(ruleName) if (ruleIndex === -1) { console.warn(`未找到名为 "${ruleName}" 的规则`) + // 如果有待处理的Promise,则拒绝它 + if (pendingJumpResolver.value) { + pendingJumpResolver.value(false); // resolve(false) 表示失败 + pendingJumpResolver.value = null; + } return false } if (props.log) console.log(`跳转到规则 "${ruleName}" (索引: ${ruleIndex})`); @@ -185,7 +189,6 @@ const jumpToRule = (ruleName: string) => { } } - // 【修复】: 如果规则是反向的,则从结束帧开始播放 const endFrame = targetRule.endFrame ?? (startFrame + targetRule.frame - 1); currentFrame.value = targetRule.reverse ? endFrame : startFrame; @@ -201,12 +204,16 @@ const jumpToRule = (ruleName: string) => { lastFrameTime.value = performance.now() animate() + // 【修改】: 跳转成功,兑现Promise + if (pendingJumpResolver.value) { + pendingJumpResolver.value(true); + pendingJumpResolver.value = null; + } + return true } -// ================================================================= -// 核心修复区域 2: startAnimation 函数 -// ================================================================= +// startAnimation 函数 (不变) const startAnimation = () => { if (!props.rules.length || !loadedImages.value.length || isPlaying.value) return @@ -225,7 +232,6 @@ const startAnimation = () => { } } - // 【修复】: 如果规则是反向的,则从结束帧开始播放 const endFrame = currentRule.endFrame ?? (startFrame + currentRule.frame - 1); currentFrame.value = currentRule.reverse ? endFrame : startFrame; @@ -235,9 +241,7 @@ const startAnimation = () => { animate() } -// ================================================================= -// 核心修复区域 3: animate 函数 -// ================================================================= +// animate 函数 (不变) const animate = () => { if (!isPlaying.value || currentRuleIndex.value >= props.rules.length) { animationId.value = undefined; @@ -253,7 +257,6 @@ const animate = () => { if (now - lastFrameTime.value >= frameDuration) { lastFrameTime.value = now - (now - lastFrameTime.value) % frameDuration; - // 1. 计算当前规则的帧范围 let startFrame = currentRule.startFrame ?? 0; if (currentRule.startFrame === undefined) { for (let i = 0; i < currentRuleIndex.value; i++) { @@ -262,78 +265,61 @@ const animate = () => { } const endFrame = currentRule.endFrame ?? (startFrame + currentRule.frame - 1); - // 2. 计算下一帧 let nextFrame = currentFrame.value + (currentRule.reverse ? -1 : 1); - // 3. 检查是否超出当前规则的边界 const isEndOfRuleSegment = currentRule.reverse ? (nextFrame < startFrame) : (nextFrame > endFrame); if (isEndOfRuleSegment) { - // 到达边界,完成了一轮循环 currentLoopCount.value++; + + if (pendingJumpTo.value) { + const targetRule = pendingJumpTo.value; + pendingJumpTo.value = undefined; + if (props.log) console.log(`软跳转触发,目标规则 "${targetRule}"`); + jumpToRule(targetRule); + return; + } const isRuleFinished = currentRule.loop > 0 && currentLoopCount.value >= currentRule.loop; if (isRuleFinished) { - // 规则已完成,准备过渡到下一条 - if (pendingJumpTo.value) { - const targetRule = pendingJumpTo.value; - pendingJumpTo.value = undefined; - jumpToRule(targetRule); - return; // jumpToRule会启动新的animate, 故此处直接返回 - } - if (currentRule.pauseAfter) { isPaused.value = true; isPlaying.value = false; if(props.log) console.log(`规则 "${currentRule.name}" 完成,暂停等待跳转指令`); - // 定格在当前规则的最后一帧 drawFrame(currentRule.reverse ? startFrame : endFrame); return; } - // 自动进入下一个规则 currentRuleIndex.value++; currentLoopCount.value = 0; if (currentRuleIndex.value >= props.rules.length) { - // 所有规则播放完毕 isPlaying.value = false; if(props.log) console.log('所有规则播放完毕'); - // 定格在最后一个规则的最后一帧 drawFrame(currentRule.reverse ? startFrame : endFrame); return; } - // 【修复】: 为下一个规则设置正确的起始帧 const nextRule = props.rules[currentRuleIndex.value]; - let nextRuleStartFrame = nextRule.startFrame ?? 0; - if (nextRule.startFrame === undefined) { - // 使用刚结束的规则的结束帧的下一帧作为起点 - nextRuleStartFrame = endFrame + 1; - } + let nextRuleStartFrame = nextRule.startFrame ?? (endFrame + 1); const nextRuleEndFrame = nextRule.endFrame ?? (nextRuleStartFrame + nextRule.frame - 1); - // 根据新规则的 reverse 属性设置 currentFrame currentFrame.value = nextRule.reverse ? nextRuleEndFrame : nextRuleStartFrame; if(props.log) console.log(`进入下一规则 "${nextRule.name}", 实际开始帧: ${currentFrame.value}`); } else { - // 规则未完成(无限循环或循环次数未到),循环播放 - // 【修复】: 重置循环时,也需要根据 reverse 属性设置正确的帧 currentFrame.value = currentRule.reverse ? endFrame : startFrame; } } else { - // 仍在当前规则内,正常播放 currentFrame.value = nextFrame; } - // 4. 绘制计算出的最终帧 drawFrame(currentFrame.value); } }; -// stopAnimation 函数 (不变) +// stopAnimation 函数 (增加清理逻辑) const stopAnimation = () => { isPlaying.value = false isPaused.value = false @@ -341,21 +327,27 @@ const stopAnimation = () => { cancelAnimationFrame(animationId.value) animationId.value = undefined } + // 【修改】: 清理待处理的跳转 + if (pendingJumpResolver.value) { + pendingJumpResolver.value(false); // 动画停止,视为跳转失败 + pendingJumpResolver.value = null; + } + pendingJumpTo.value = undefined; } -// resetAnimation 函数 (不变) +// resetAnimation 函数 (增加清理逻辑) const resetAnimation = () => { stopAnimation() currentRuleIndex.value = 0 currentFrame.value = 0 currentLoopCount.value = 0 - pendingJumpTo.value = undefined + // pendingJumpTo 和 pendingJumpResolver 已在 stopAnimation 中清理 if (loadedImages.value.length > 0) { drawFrame(0) } } -// togglePlayback, setJumpTarget, jumpToRuleImmediately, resumeFromPause (不变) +// togglePlayback, resumeFromPause (不变) const togglePlayback = () => { if (isPlaying.value) { stopAnimation() @@ -364,18 +356,6 @@ const togglePlayback = () => { } } -const setJumpTarget = (ruleName: string) => { - if (isPaused.value) { - jumpToRule(ruleName) - } else { - pendingJumpTo.value = ruleName - } -} - -const jumpToRuleImmediately = (ruleName: string) => { - return jumpToRule(ruleName) -} - const resumeFromPause = () => { if (isPaused.value) { isPaused.value = false @@ -385,12 +365,55 @@ const resumeFromPause = () => { } } +// ================================================================= +// 核心修改区域 2: 跳转方法 +// ================================================================= +/** + * 立即跳转到指定规则,会打断当前动画。 + * @param {string} ruleName 要跳转到的规则名。 + */ +const jumpTo = (ruleName: string) => { + return jumpToRule(ruleName) +} -// watch 和 onMounted (微调清理逻辑) +/** + * “软”跳转。该跳转会等到当前规则循环结束后再执行。 + * 如果动画已暂停或停止,则立即跳转。 + * @param {string} ruleName 要跳转到的规则名。 + * @returns {Promise} 一个Promise,当成功跳转到目标规则时 resolve(true),否则 resolve(false)。 + */ +const jumpToSoftly = (ruleName: string): Promise => { + return new Promise((resolve) => { + const ruleIndex = findRuleIndex(ruleName); + if (ruleIndex === -1) { + console.warn(`无法设置软跳转:未找到规则 "${ruleName}"。`); + resolve(false); + return; + } + + // 如果之前有待处理的跳转,先将其标记为失败 + if (pendingJumpResolver.value) { + pendingJumpResolver.value(false); + } + // 设置新的 Promise resolver + pendingJumpResolver.value = resolve; + + if (isPaused.value || !isPlaying.value) { + if(props.log) console.log(`动画已暂停或停止。立即执行跳转到 "${ruleName}"。`); + // jumpToRule 将会调用并清理 pendingJumpResolver + jumpToRule(ruleName); + } else if (isPlaying.value) { + if(props.log) console.log(`已计划软跳转到 "${ruleName}"。将在当前循环结束后触发。`); + pendingJumpTo.value = ruleName; + } + }); +} + + +// watch 和生命周期钩子 (不变) watch(() => props.url, (newUrl) => { if (newUrl) { - stopAnimation() - // 清理旧的图片数据 + resetAnimation() // 使用 reset 来确保清理 images.value.forEach(url => URL.revokeObjectURL(url)); images.value = []; loadedImages.value = []; @@ -408,16 +431,16 @@ watch(() => props.rules, () => { }, { deep: true }) onBeforeUnmount(() => { - stopAnimation(); - // 组件卸载时,确保释放所有Blob URL + resetAnimation(); // 使用 reset 来确保清理 images.value.forEach(url => URL.revokeObjectURL(url)); }); -// findRuleIndex 和 defineExpose (不变) +// findRuleIndex (不变) const findRuleIndex = (ruleName: string): number => { return props.rules.findIndex(rule => rule.name === ruleName) } +// defineExpose (修改) defineExpose({ images, loadedImages, @@ -434,8 +457,10 @@ defineExpose({ reset: resetAnimation, toggle: togglePlayback, resume: resumeFromPause, - jumpTo: jumpToRuleImmediately, - setJumpTarget, + /** 立即跳转,会打断当前动画。 */ + jumpTo, + /** 在当前动画循环结束后跳转,返回一个Promise。 */ + jumpToSoftly, drawFrame: (frameIndex: number) => drawFrame(frameIndex), findRuleIndex, getRules: () => props.rules, diff --git a/src/composables/AniLoader.vue b/src/composables/AniLoader.vue deleted file mode 100644 index 19c45de..0000000 --- a/src/composables/AniLoader.vue +++ /dev/null @@ -1,168 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/composables/AniPlayer.vue b/src/composables/AniPlayer.vue deleted file mode 100644 index 35d2e5b..0000000 --- a/src/composables/AniPlayer.vue +++ /dev/null @@ -1,302 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/composables/animation-store.ts b/src/composables/animation-store.ts deleted file mode 100644 index f177c60..0000000 --- a/src/composables/animation-store.ts +++ /dev/null @@ -1,23 +0,0 @@ -// src/composables/animation-store.ts (or wherever you prefer) -import { reactive } from 'vue' -import type { InjectionKey } from 'vue' - -// Defines the data structure for a single loaded animation -export interface AnimationData { - status: 'idle' | 'loading' | 'loaded' | 'error'; - frames: ImageData[]; - width: number; - height: number; - totalFrames: number; - error?: string; - progress: number; -} - -// Defines the store, which is a map from a unique animation ID to its data -export type AnimationStore = Map - -// Create a reactive store instance -export const animationStore = reactive(new Map()) - -// Create a unique Symbol as the injection key. This is best practice. -export const AnimationStoreKey: InjectionKey = Symbol('AnimationStore') \ No newline at end of file diff --git a/src/pages/Game.vue b/src/pages/Game.vue index a8a3e04..6d25944 100644 --- a/src/pages/Game.vue +++ b/src/pages/Game.vue @@ -16,15 +16,15 @@ const sunEle = useTemplateRef('sun-ani'); const sunEndEle = useTemplateRef('sun-ani-end'); const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); - +const emit = defineEmits(['cloudUp', 'cloudDown', 'restart']); async function shoot() { //sunEle.value?.jumpTo('天上飞') sunEle.value?.jumpTo('蓄力飞') await document.querySelector('.sun-ani-wrapper')?.animate([ - { transform: 'translateY(0)' }, - { transform: 'translateY(8%)' }, + { transform: 'translateY(0) translateX(-50%)' }, + { transform: 'translateY(8%) translateX(-50%)' }, ], { duration: 900, easing: 'linear', @@ -41,15 +41,7 @@ async function shoot() { }).finished.then(() => { document.querySelector('.arrow')?.remove(); }); - document.querySelectorAll('.cloud').forEach((ele) => { - ele.animate([ - { top: '300vw' }, - ], { - duration: 1000, - easing: 'ease-in', - fill: 'forwards', - }); - }); + emit('cloudUp'); document.querySelector('.bed')?.animate([ { transform: 'translateY(0)' }, { transform: 'translateY(150%)' }, @@ -61,38 +53,30 @@ async function shoot() { }); await document.querySelector('.sun-ani-wrapper')?.animate([ - { transform: 'translateY(8%)' }, - { transform: 'translateY(-130%)' }, + { transform: 'translateY(8%) translateX(-50%)', width: '60vw' }, + { transform: 'translateY(-180%) translateX(-50%)', width: '50vw' }, ], { - duration: 3000, - easing: 'ease-in-out', + duration: 3200, + easing: 'cubic-bezier(0.99, 0.13, 0.35, 0.74)', fill: 'forwards', }).finished - document.querySelectorAll('.cloud.all').forEach((ele) => { - ele.animate([ - { top: '-50vw' }, - // @ts-ignore - { top: ele.style.top }, - ], { - duration: 1000, - easing: 'ease-in', - fill: 'forwards', - }); - }); - canAction.value = true; - gameLoop(); - - + emit('cloudDown'); await document.querySelector('.sun-ani-wrapper')?.animate([ - { transform: 'translateY(-130%)' }, - { transform: 'translateY(-70%)' }, + { transform: 'translateY(-180%) translateX(-50%)' }, + { transform: 'translateY(-70%) translateX(-50%)' }, ], { - duration: 2200, - easing: 'ease', + duration: 1200, + easing: 'cubic-bezier(0.22, 0.61, 0.36, 1)', fill: 'forwards', }).finished + /* await wait(1000) */ + canAction.value = true; + + + gameLoop(); + } const gPos: Ref<'left' | 'center' | 'right'> = ref('center'); const gDeg = computed(() => ({ @@ -101,7 +85,7 @@ const gDeg = computed(() => ({ right: 8 })[gPos.value]); -const SPEED = 1.2; +const SPEED = 1.8; const transitionOn = ref(true); let lastTime = 0; @@ -118,7 +102,9 @@ const calcGiftDis = () => { } else return 0; } -const collisionThreshold = 20; // vw 单位 +const collisionThreshold = 10; // vw 单位 +const isPlayingDropAni = ref(false); + function gameLoop(currentTime = 0) { let pauseFlag = false; @@ -127,7 +113,7 @@ function gameLoop(currentTime = 0) { giftPos.value = [null, null]; giftShow.value = false; lastTime = currentTime; - if (giftProgress.value >= giftList.length ) { + if (giftProgress.value >= giftList.length) { // 游戏结束逻辑 console.log('游戏结束!耗时:', timeSpent.value / 1000, '秒'); gameEnd() @@ -141,55 +127,94 @@ function gameLoop(currentTime = 0) { timeSpent.value += deltaTime // 移动距离 = 速度 * 时间间隔 / 16.67 (目标帧率约60fps) - sunPos.value[1] -= SPEED * 0.3 * (deltaTime / 16.67); - if (sunPos.value[1] < -100) { + if (sunPos.value[1] < -50) { transitionOn.value = false; - sunPos.value[1] = 154; - } else { + if (!isPlayingDropAni.value) + (async () => { + isPlayingDropAni.value = true; + + // sunPos.y to -150, than y to 174 and drop to 124 + transitionOn.value = true; + while (sunPos.value[1] > -150) { + // 逐渐加速 + sunPos.value[1] -= SPEED * (deltaTime / 16.67) * 4; + await wait(16.67 / 2); + } + sunPos.value[1] = -150; + // 等待0.5秒 + + transitionOn.value = false; + await wait(500); + + sunPos.value[1] = 124; + await wait(0); + transitionOn.value = true; + while (sunPos.value[1] > 66) { + sunPos.value[1] -= SPEED * (deltaTime / 16.67) * 6; + await wait(16.67 / 2); + } + // 最终位置 y = 66 + sunPos.value[1] = 66; + isPlayingDropAni.value = false; + + })(); + + } else if (!isPlayingDropAni.value) { transitionOn.value = true; + sunPos.value[1] -= SPEED * 0.3 * (deltaTime / 16.67); } - if (giftPos.value[0] === null) { - // 一直生成礼物,直到距离大于 collisionThreshold * 1.5 - while (true) { - // randomly generate gift - // x: -30 - 30 - // y: -20 - 60 - giftPos.value[0] = Math.random() * 60 - 30; - giftPos.value[1] = Math.random() * 80 - 20; - if (calcGiftDis() > collisionThreshold * 1.5) { - break; // 距离足够远,退出循环 - } - } - } + const moveGift = () => { + if (giftPos.value[0] === null) { + // 一直生成礼物,直到距离大于 collisionThreshold * 1.5 + while (true) { + // 生成起始点 + giftStartPos.value[0] = Math.random() * 60 - 30; + giftStartPos.value[1] = Math.random() * 80 - 20; - // gift slowly fly to the edge - if (giftPos.value[0] !== null && giftPos.value[1] !== null) { - const giftSpeed = 0.3 * (deltaTime / 16.67); + // 生成终点 + giftEndPos.value[0] = Math.random() * 60 - 30; + giftEndPos.value[1] = Math.random() * 80 - 20; - // 根据礼物的位置决定移动方向 - if (giftPos.value[0] < 0) { - // 向左边缘移动 - giftPos.value[0] -= giftSpeed * 0.2; - if (giftPos.value[0] < -45) { - giftPos.value[0] = -45; // 到达左边缘后停止 - } - } else { - // 向右边缘移动 - giftPos.value[0] += giftSpeed * 0.2; - if (giftPos.value[0] > 45) { - giftPos.value[0] = 45; // 到达右边缘后停止 + // 初始位置为起始点 + giftPos.value[0] = giftStartPos.value[0]; + giftPos.value[1] = giftStartPos.value[1]; + + // 重置移动进度和方向 + giftMoveProgress.value = 0; + giftMoveDirection.value = 1; + + if (calcGiftDis() > collisionThreshold * 1.5) { + break; // 距离足够远,退出循环 + } } } - // y 方向始终向上移动 - giftPos.value[1] += giftSpeed * 0.3; - if (giftPos.value[1] > 60) { - giftPos.value[1] = 60; // 到达上边缘后停止 + // gift move between two points + if (giftPos.value[0] !== null && giftPos.value[1] !== null) { + const giftSpeed = 0.8 * (deltaTime / 16.67); // 速度加快 (原来是0.3) + + // 更新移动进度 + giftMoveProgress.value += giftSpeed * giftMoveDirection.value * 0.01; + + // 检查是否到达端点,如果是则反转方向 + if (giftMoveProgress.value >= 1) { + giftMoveProgress.value = 1; + giftMoveDirection.value = -1; + } else if (giftMoveProgress.value <= 0) { + giftMoveProgress.value = 0; + giftMoveDirection.value = 1; + } + + // 使用线性插值计算当前位置 + giftPos.value[0] = giftStartPos.value[0] + (giftEndPos.value[0] - giftStartPos.value[0]) * giftMoveProgress.value; + giftPos.value[1] = giftStartPos.value[1] + (giftEndPos.value[1] - giftStartPos.value[1]) * giftMoveProgress.value; } + } + moveGift(); // 计算太阳和礼物之间的距离 const distance = calcGiftDis() @@ -199,9 +224,7 @@ function gameLoop(currentTime = 0) { console.log('太阳收集到礼物!', { sunPos: sunPos.value, giftPos: giftPos.value, distance }); pauseFlag = true; // 暂停游戏循环 - // TODO: 在这里添加碰撞后的逻辑 - // 例如:播放音效、更新分数、显示特效、切换到下一个礼物等 - giftShow.value = true; // 隐藏礼物 + giftShow.value = true; // 显示展示礼物界面 document.querySelector('.gift-item.show')?.animate([ { transform: 'scale(1)' }, { transform: 'scale(0)' }, @@ -210,7 +233,7 @@ function gameLoop(currentTime = 0) { easing: 'cubic-bezier(0.42,-0.98, 0.4, 1)', fill: 'forwards', }).finished.then(() => { - giftPos.value = [null, null]; // 隐藏礼物 + giftPos.value = [null, null]; // 重置游戏中礼物 }); } @@ -230,6 +253,18 @@ function gToggle(pos: 'left' | 'center' | 'right') { fill: 'forwards', }); + if (isPlayingDropAni.value) { + // 如果正在下落动画中,直接返回 + return; + } + + if (sunPos.value[0] < -36 + || sunPos.value[0] > 36 + || sunPos.value[1] > 70) { + // 太阳位置不在范围内,直接返回 + return; + } + const windAniMap: Record<'left' | 'center' | 'right', [number, [number, number], [number, number]]> = { left: [ -60, @@ -270,6 +305,12 @@ function gToggle(pos: 'left' | 'center' | 'right') { })[pos].forEach((v, i) => { sunPos.value[i] += v * SPEED * 2; }); + + if (sunPos.value[0] < -36) { + sunPos.value[0] = -36; + } else if (sunPos.value[0] > 36) { + sunPos.value[0] = 36; + } } const giftShow = ref(false); @@ -280,6 +321,10 @@ const canAction = ref(false); const sunPos = ref([0, 0]) // x, y const giftPos: Ref<[number | null, number | null]> = ref([null, null]); // x, y +const giftStartPos: Ref<[number, number]> = ref([0, 0]); // 起始点 +const giftEndPos: Ref<[number, number]> = ref([0, 0]); // 终点 +const giftMoveProgress = ref(0); // 移动进度 0-1 +const giftMoveDirection = ref(1); // 移动方向 1正向 -1反向 const giftList = assets.icons; @@ -469,6 +514,30 @@ const showEndAni = ref(false); async function gameEnd() { + fetch('/api/score', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + region: props.userdata.region, + store: props.userdata.store, + name: props.userdata.username, + time: timeSpent.value / 1000, + }) + }).then(_ => { + fetch('/api/score', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(res => res.json()) + .then(data => { + leaderBoard.value = data; + }); + }) + document.querySelector('.bed')?.animate([ { transform: 'translateY(150%)' }, { transform: 'translateY(0)' }, @@ -485,14 +554,15 @@ async function gameEnd() { img.style.animationDirection = 'reverse'; }); - await document.querySelector('.sun-ani-wrapper')?.animate([ - { transform: 'translateY(8%)', left: '19%', bottom: '23%' }, + document.querySelector('.sun-ani-wrapper')?.animate([ + { transform: 'translateY(8%) translateX(-50%)', left: '50%', bottom: '23%', width: '60vw' }, ], { - duration: 700, + duration: 1800, easing: 'ease-in-out', fill: 'forwards', }).finished + isGameEnd.value = true; // 模拟图标聚集并获取最终位置 @@ -501,13 +571,7 @@ async function gameEnd() { await wait(0) floatIcons.value = finalPositions; - const element = document.querySelector('.sun-ani-wrapper'); - if (!element) return; - - await element.animate( - { transform: 'translateY(8%)' }, - { duration: 2500, easing: 'ease-in-out', fill: 'forwards' } - ).finished; + await wait(1800) showEndAni.value = true; sunEndEle.value?.jumpTo('all'); @@ -530,12 +594,6 @@ async function gameEnd() { } -/* setTimeout(() => { - canAction.value = true - gameEnd() - -}, 100); */ - const leaderBoard = ref([ { name: '始祖鸟', time: 50, region: '奥莱北区', store: "上海始祖鸟阿尔法中心" }, @@ -559,15 +617,13 @@ const showScore = ref(false) const showLastPage = ref(false); - +const sunAniEndRules = ref([ + { name: 'all', frame: 165, loop: 1, pauseAfter: false, duration: 33 }, + { name: '左右跳', frame: 66, loop: 0, pauseAfter: true, duration: 33 }, + { name: '落下', frame: 32, loop: 1, pauseAfter: false, duration: 33 }, + { name: 'loop', frame: 33, loop: 0, pauseAfter: true, duration: 33 }, +]) async function finishCollect() { - document.querySelector('.bar-container')?.animate([ - { transform: 'translateY(300%)' }], { - duration: 500, - easing: 'ease-in-out', - fill: 'forwards', - }) - document.querySelector('.finish-collect')?.animate([ { transform: 'scale(1)' }, { transform: 'scale(0)' }, @@ -591,48 +647,98 @@ async function finishCollect() { easing: 'ease-in-out', fill: 'forwards', }).finished + await sunEndEle.value?.jumpToSoftly('落下'); + document.querySelector('.bar-container')?.animate([ + { transform: 'translateY(300%)' }], { + duration: 500, + easing: 'ease-in-out', + fill: 'forwards', + }) + showLastPage.value = true; } + +// 重置游戏状态的函数 +function resetGame() { + return + // 重置所有响应式状态到初始值 + gPos.value = 'center'; + transitionOn.value = true; + lastTime = 0; + giftShow.value = false; + canAction.value = false; + sunPos.value = [0, 0]; + giftPos.value = [null, null]; + giftStartPos.value = [0, 0]; + giftEndPos.value = [0, 0]; + giftMoveProgress.value = 0; + giftMoveDirection.value = 1; + giftProgress.value = 0; + isGameEnd.value = false; + timeSpent.value = 0; + showEndAni.value = false; + showScore.value = false; + showLastPage.value = false; + + // 重新生成图标位置 + floatIcons.value = generateIconPositions(); + + // 重置动画元素到初始状态 + sunEle.value?.jumpTo('起飞前'); + sunEndEle.value?.jumpTo('loop'); + + // 清除所有动画和样式 + const elementsToReset = [ + '.bed', '.sun-ani-wrapper', '.p-machine', '.action img', + '.gift-item', '.wind', '.bar-container', '.finish-collect', + '.wecare-title', '.lines' + ]; + + elementsToReset.forEach(selector => { + const element = document.querySelector(selector); + if (element instanceof HTMLElement) { + element.getAnimations().forEach(animation => animation.cancel()); + element.style.animation = ''; + element.style.transform = ''; + element.style.opacity = ''; + } + }); +}