From ca95c605620523bbd903a3a322268cb7e8d169d0 Mon Sep 17 00:00:00 2001 From: "nise.moe" Date: Sun, 3 Mar 2024 16:22:03 +0100 Subject: [PATCH] Added nise-replay-viewer --- .../kotlin/com/nisemoe/nise/rss/RssService.kt | 3 +- nise-frontend/Build.sh | 6 - nise-replay-viewer/Build.sh | 33 + nise-replay-viewer/Dockerfile | 10 + nise-replay-viewer/README.md | 46 + nise-replay-viewer/components.json | 16 + nise-replay-viewer/dp12.png | Bin 0 -> 24396 bytes nise-replay-viewer/index.html | 26 + nise-replay-viewer/nginx.conf | 29 + nise-replay-viewer/osu-parsers/.eslintignore | 2 + nise-replay-viewer/osu-parsers/.eslintrc.json | 180 + nise-replay-viewer/osu-parsers/.npmignore | 9 + nise-replay-viewer/osu-parsers/LICENSE | 20 + nise-replay-viewer/osu-parsers/README.md | 243 + .../osu-parsers/lib/browser.mjs | 3282 ++++++++++++++ nise-replay-viewer/osu-parsers/lib/index.d.ts | 528 +++ nise-replay-viewer/osu-parsers/package.json | 65 + nise-replay-viewer/package-lock.json | 3898 +++++++++++++++++ nise-replay-viewer/package.json | 52 + nise-replay-viewer/postcss.config.js | 6 + nise-replay-viewer/public/ctrl.svg | 5 + nise-replay-viewer/public/cursor.png | Bin 0 -> 9632 bytes nise-replay-viewer/public/cursortrail.png | Bin 0 -> 1916 bytes nise-replay-viewer/public/default0.png | Bin 0 -> 3357 bytes nise-replay-viewer/public/default1.png | Bin 0 -> 624 bytes nise-replay-viewer/public/default2.png | Bin 0 -> 2513 bytes nise-replay-viewer/public/default3.png | Bin 0 -> 2907 bytes nise-replay-viewer/public/default4.png | Bin 0 -> 1503 bytes nise-replay-viewer/public/default5.png | Bin 0 -> 3009 bytes nise-replay-viewer/public/default6.png | Bin 0 -> 2957 bytes nise-replay-viewer/public/default7.png | Bin 0 -> 1353 bytes nise-replay-viewer/public/default8.png | Bin 0 -> 3532 bytes nise-replay-viewer/public/default9.png | Bin 0 -> 3106 bytes nise-replay-viewer/public/ekey.svg | 5 + nise-replay-viewer/public/fav.png | Bin 0 -> 24396 bytes nise-replay-viewer/public/gitlogo.png | Bin 0 -> 9257 bytes nise-replay-viewer/public/hitcircle.png | Bin 0 -> 11843 bytes .../public/hitcircleoverlay.png | Bin 0 -> 22077 bytes nise-replay-viewer/public/icon.png | Bin 0 -> 24396 bytes nise-replay-viewer/public/mod_dt.png | Bin 0 -> 1531 bytes nise-replay-viewer/public/mod_ez.png | Bin 0 -> 1411 bytes nise-replay-viewer/public/mod_fl.png | Bin 0 -> 1111 bytes nise-replay-viewer/public/mod_hd.png | Bin 0 -> 1722 bytes nise-replay-viewer/public/mod_hr.png | Bin 0 -> 1386 bytes nise-replay-viewer/public/mod_ht.png | Bin 0 -> 1381 bytes nise-replay-viewer/public/mod_nc.png | Bin 0 -> 1386 bytes nise-replay-viewer/public/mod_nf.png | Bin 0 -> 1222 bytes nise-replay-viewer/public/mod_nm.png | Bin 0 -> 1184 bytes nise-replay-viewer/public/mod_pf.png | Bin 0 -> 1110 bytes nise-replay-viewer/public/mod_sd.png | Bin 0 -> 1291 bytes nise-replay-viewer/public/mouse.svg | 6 + nise-replay-viewer/public/qkey.svg | 5 + nise-replay-viewer/public/radix.png | Bin 0 -> 19462 bytes nise-replay-viewer/public/scroll.svg | 7 + nise-replay-viewer/public/sliderb0.png | Bin 0 -> 7247 bytes .../public/sliderfollowcircle.png | Bin 0 -> 22541 bytes nise-replay-viewer/public/space.svg | 5 + nise-replay-viewer/public/vite.svg | 1 + nise-replay-viewer/src/decorators/hook.ts | 11 + .../src/decorators/singleton.ts | 14 + nise-replay-viewer/src/globals.css | 76 + nise-replay-viewer/src/hooks.ts | 38 + .../src/hooks/canvasControls.ts | 63 + nise-replay-viewer/src/hooks/osuControls.ts | 46 + nise-replay-viewer/src/hooks/p5Base.ts | 47 + nise-replay-viewer/src/hooks/starter.ts | 4 + nise-replay-viewer/src/hooks/tooling.ts | 29 + nise-replay-viewer/src/interface/App.tsx | 38 + .../interface/components/theme-provider.tsx | 73 + .../src/interface/components/ui/badge.tsx | 36 + .../src/interface/components/ui/button.tsx | 56 + .../src/interface/components/ui/card.tsx | 79 + .../interface/components/ui/context-menu.tsx | 198 + .../src/interface/components/ui/dialog.tsx | 120 + .../src/interface/components/ui/input.tsx | 25 + .../src/interface/components/ui/label.tsx | 24 + .../src/interface/components/ui/menubar.tsx | 234 + .../src/interface/components/ui/sheet.tsx | 138 + .../src/interface/components/ui/slider.tsx | 26 + .../src/interface/components/ui/sonner.tsx | 29 + .../src/interface/composites/Menu.tsx | 82 + .../src/interface/composites/about-dialog.tsx | 27 + .../interface/composites/analysis.-sheet.tsx | 121 + .../src/interface/composites/helper.tsx | 35 + .../src/interface/composites/song-slider.tsx | 60 + nise-replay-viewer/src/lib/utils.ts | 6 + nise-replay-viewer/src/main.tsx | 46 + nise-replay-viewer/src/osu/Analysis.ts | 278 ++ nise-replay-viewer/src/osu/Drawer.ts | 299 ++ nise-replay-viewer/src/osu/OsuRenderer.ts | 413 ++ nise-replay-viewer/src/renderer.ts | 50 + nise-replay-viewer/src/style.css | 35 + nise-replay-viewer/src/tooling/brush.ts | 15 + nise-replay-viewer/src/tooling/tool.ts | 5 + nise-replay-viewer/src/utils.ts | 84 + nise-replay-viewer/tailwind.config.js | 76 + nise-replay-viewer/tsconfig.json | 25 + nise-replay-viewer/vite.config.ts | 12 + nise-replay-viewer/yarn.lock | 2011 +++++++++ 99 files changed, 13564 insertions(+), 8 deletions(-) create mode 100755 nise-replay-viewer/Build.sh create mode 100644 nise-replay-viewer/Dockerfile create mode 100644 nise-replay-viewer/README.md create mode 100644 nise-replay-viewer/components.json create mode 100644 nise-replay-viewer/dp12.png create mode 100644 nise-replay-viewer/index.html create mode 100644 nise-replay-viewer/nginx.conf create mode 100644 nise-replay-viewer/osu-parsers/.eslintignore create mode 100644 nise-replay-viewer/osu-parsers/.eslintrc.json create mode 100644 nise-replay-viewer/osu-parsers/.npmignore create mode 100644 nise-replay-viewer/osu-parsers/LICENSE create mode 100644 nise-replay-viewer/osu-parsers/README.md create mode 100644 nise-replay-viewer/osu-parsers/lib/browser.mjs create mode 100644 nise-replay-viewer/osu-parsers/lib/index.d.ts create mode 100644 nise-replay-viewer/osu-parsers/package.json create mode 100644 nise-replay-viewer/package-lock.json create mode 100644 nise-replay-viewer/package.json create mode 100644 nise-replay-viewer/postcss.config.js create mode 100644 nise-replay-viewer/public/ctrl.svg create mode 100644 nise-replay-viewer/public/cursor.png create mode 100644 nise-replay-viewer/public/cursortrail.png create mode 100644 nise-replay-viewer/public/default0.png create mode 100644 nise-replay-viewer/public/default1.png create mode 100644 nise-replay-viewer/public/default2.png create mode 100644 nise-replay-viewer/public/default3.png create mode 100644 nise-replay-viewer/public/default4.png create mode 100644 nise-replay-viewer/public/default5.png create mode 100644 nise-replay-viewer/public/default6.png create mode 100644 nise-replay-viewer/public/default7.png create mode 100644 nise-replay-viewer/public/default8.png create mode 100644 nise-replay-viewer/public/default9.png create mode 100644 nise-replay-viewer/public/ekey.svg create mode 100644 nise-replay-viewer/public/fav.png create mode 100644 nise-replay-viewer/public/gitlogo.png create mode 100644 nise-replay-viewer/public/hitcircle.png create mode 100644 nise-replay-viewer/public/hitcircleoverlay.png create mode 100644 nise-replay-viewer/public/icon.png create mode 100644 nise-replay-viewer/public/mod_dt.png create mode 100644 nise-replay-viewer/public/mod_ez.png create mode 100644 nise-replay-viewer/public/mod_fl.png create mode 100644 nise-replay-viewer/public/mod_hd.png create mode 100644 nise-replay-viewer/public/mod_hr.png create mode 100644 nise-replay-viewer/public/mod_ht.png create mode 100644 nise-replay-viewer/public/mod_nc.png create mode 100644 nise-replay-viewer/public/mod_nf.png create mode 100644 nise-replay-viewer/public/mod_nm.png create mode 100644 nise-replay-viewer/public/mod_pf.png create mode 100644 nise-replay-viewer/public/mod_sd.png create mode 100644 nise-replay-viewer/public/mouse.svg create mode 100644 nise-replay-viewer/public/qkey.svg create mode 100644 nise-replay-viewer/public/radix.png create mode 100644 nise-replay-viewer/public/scroll.svg create mode 100644 nise-replay-viewer/public/sliderb0.png create mode 100644 nise-replay-viewer/public/sliderfollowcircle.png create mode 100644 nise-replay-viewer/public/space.svg create mode 100644 nise-replay-viewer/public/vite.svg create mode 100644 nise-replay-viewer/src/decorators/hook.ts create mode 100644 nise-replay-viewer/src/decorators/singleton.ts create mode 100644 nise-replay-viewer/src/globals.css create mode 100644 nise-replay-viewer/src/hooks.ts create mode 100644 nise-replay-viewer/src/hooks/canvasControls.ts create mode 100644 nise-replay-viewer/src/hooks/osuControls.ts create mode 100644 nise-replay-viewer/src/hooks/p5Base.ts create mode 100644 nise-replay-viewer/src/hooks/starter.ts create mode 100644 nise-replay-viewer/src/hooks/tooling.ts create mode 100644 nise-replay-viewer/src/interface/App.tsx create mode 100644 nise-replay-viewer/src/interface/components/theme-provider.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/badge.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/button.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/card.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/context-menu.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/dialog.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/input.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/label.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/menubar.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/sheet.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/slider.tsx create mode 100644 nise-replay-viewer/src/interface/components/ui/sonner.tsx create mode 100644 nise-replay-viewer/src/interface/composites/Menu.tsx create mode 100644 nise-replay-viewer/src/interface/composites/about-dialog.tsx create mode 100644 nise-replay-viewer/src/interface/composites/analysis.-sheet.tsx create mode 100644 nise-replay-viewer/src/interface/composites/helper.tsx create mode 100644 nise-replay-viewer/src/interface/composites/song-slider.tsx create mode 100644 nise-replay-viewer/src/lib/utils.ts create mode 100644 nise-replay-viewer/src/main.tsx create mode 100644 nise-replay-viewer/src/osu/Analysis.ts create mode 100644 nise-replay-viewer/src/osu/Drawer.ts create mode 100644 nise-replay-viewer/src/osu/OsuRenderer.ts create mode 100644 nise-replay-viewer/src/renderer.ts create mode 100644 nise-replay-viewer/src/style.css create mode 100644 nise-replay-viewer/src/tooling/brush.ts create mode 100644 nise-replay-viewer/src/tooling/tool.ts create mode 100644 nise-replay-viewer/src/utils.ts create mode 100644 nise-replay-viewer/tailwind.config.js create mode 100644 nise-replay-viewer/tsconfig.json create mode 100644 nise-replay-viewer/vite.config.ts create mode 100644 nise-replay-viewer/yarn.lock diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/rss/RssService.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/rss/RssService.kt index efe95ef..4d75684 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/rss/RssService.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/rss/RssService.kt @@ -13,8 +13,7 @@ import java.util.* @Service class RssService( - private val dslContext: DSLContext, - private val scoreService: ScoreService + private val dslContext: DSLContext ) { fun generateFeed(): RssFeed { diff --git a/nise-frontend/Build.sh b/nise-frontend/Build.sh index c5b140d..25d3264 100755 --- a/nise-frontend/Build.sh +++ b/nise-frontend/Build.sh @@ -5,12 +5,6 @@ IMAGE_NAME="nise-frontend" IMAGE_REGISTRY="git.gengo.tech/nuff" IMAGE_VERSION="latest" -# Check if there are uncommitted changes -if [[ -n $(git status --porcelain) ]]; then - echo "Error: There are uncommitted changes. Please commit them before building." - exit 1 -fi - rm -rf target/ # Clean and build Angular project diff --git a/nise-replay-viewer/Build.sh b/nise-replay-viewer/Build.sh new file mode 100755 index 0000000..ac1974c --- /dev/null +++ b/nise-replay-viewer/Build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Set variables +IMAGE_NAME="nise-replay-viewer" +IMAGE_REGISTRY="git.gengo.tech/nuff" +IMAGE_VERSION="latest" + +rm -rf dist/ + +# Clean and build Angular project +source /usr/share/nvm/init-nvm.sh +nvm use 18.19 +npm run build +if [ $? -ne 0 ]; then + echo "ng build failed" + exit 1 +fi + +# Build and push Docker image +docker build . -t $IMAGE_NAME:$IMAGE_VERSION +if [ $? -ne 0 ]; then + echo "Docker build failed" + exit 1 +fi + +docker tag $IMAGE_NAME:$IMAGE_VERSION $IMAGE_REGISTRY/$IMAGE_NAME:$IMAGE_VERSION +docker push $IMAGE_REGISTRY/$IMAGE_NAME:$IMAGE_VERSION +if [ "$?" != "0" ]; then + echo "Error: Failed to push $IMAGE_REGISTRY/$IMAGE_NAME:$IMAGE_VERSION" + exit 1 +fi + +echo "Docker image pushed successfully to $IMAGE_REGISTRY/$IMAGE_NAME:$IMAGE_VERSION" diff --git a/nise-replay-viewer/Dockerfile b/nise-replay-viewer/Dockerfile new file mode 100644 index 0000000..f9f0f98 --- /dev/null +++ b/nise-replay-viewer/Dockerfile @@ -0,0 +1,10 @@ +FROM openresty/openresty:focal + +RUN rm -rf /usr/share/nginx/html/* + +COPY dist/ /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/nise-replay-viewer/README.md b/nise-replay-viewer/README.md new file mode 100644 index 0000000..7b60d67 --- /dev/null +++ b/nise-replay-viewer/README.md @@ -0,0 +1,46 @@ +![](./public/gitlogo.png) + +![](https://img.shields.io/badge/status-in_development-blue) + +Replay Inspector is a beatmap/replay analyzer and editor for [osu!](https://osu.ppy.sh/) and is currently in development phase. + +> Inspector is NOT a fork of abstrakt8's rewind, and it has been developed from ground up using same utilites. This project was developed with simplicity in mind using beginner friendly frameworks and easy structure. + +## Geting started + +To use the public version of Replay Inspector, you can follow [click](https://edit.assist.games). + +If you wanna clone and use it locally, proceed by + +``` +git clone https://github.com/cunev/replay-inspector +``` + +then install packages + +``` +bun install +``` + +and run the editor + + +``` +bun run dev +``` + +## Features +- View replay path and beatmap +- Seek through replay +- View clicks +- Enable/disable hardrock +- Modify map metadata +- Analyze clicks, frametimes and release times + +## In development +- Export replays +- Modify path +- Modify keypresses +- Music playback +- Shortcuts + diff --git a/nise-replay-viewer/components.json b/nise-replay-viewer/components.json new file mode 100644 index 0000000..29ca57c --- /dev/null +++ b/nise-replay-viewer/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/globals.css", + "baseColor": "neutral", + "cssVariables": true + }, + "aliases": { + "components": "@/interface/components", + "utils": "@/lib/utils" + } +} \ No newline at end of file diff --git a/nise-replay-viewer/dp12.png b/nise-replay-viewer/dp12.png new file mode 100644 index 0000000000000000000000000000000000000000..43617fbef0b5b15fd1ce939953b2aefb7ea59cc1 GIT binary patch literal 24396 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∋Env~6y~ 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Ν4PoPF)#8=`FykJ`zK9B!du?eD`9pS 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 + + + + + + Replay Inspector + + + + + + + + + + + + + +
+
+ + + + \ No newline at end of file diff --git a/nise-replay-viewer/nginx.conf b/nise-replay-viewer/nginx.conf new file mode 100644 index 0000000..0c8f3b4 --- /dev/null +++ b/nise-replay-viewer/nginx.conf @@ -0,0 +1,29 @@ +server { + + gzip on; + gzip_static on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + gzip_proxied no-cache no-store private expired auth; + gzip_min_length 1000; + + listen 80; + + resolver local=on ipv6=off; + resolver_timeout 5s; + + root /usr/share/nginx/html; + + location ~ /index.html { + add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + } + + location ~ .*\.css$|.*\.js$ { + add_header Cache-Control 'max-age=31449600'; + } + + location / { + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + +} diff --git a/nise-replay-viewer/osu-parsers/.eslintignore b/nise-replay-viewer/osu-parsers/.eslintignore new file mode 100644 index 0000000..d82b4f2 --- /dev/null +++ b/nise-replay-viewer/osu-parsers/.eslintignore @@ -0,0 +1,2 @@ +/** +!/src \ No newline at end of file diff --git a/nise-replay-viewer/osu-parsers/.eslintrc.json b/nise-replay-viewer/osu-parsers/.eslintrc.json new file mode 100644 index 0000000..3e006a5 --- /dev/null +++ b/nise-replay-viewer/osu-parsers/.eslintrc.json @@ -0,0 +1,180 @@ +{ + "env": { + "node": true + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 11 + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "plugins": [ + "@typescript-eslint" + ], + "rules": { + /* Best Practices */ + "accessor-pairs": ["error", { + "setWithoutGet": true + }], + "curly": ["error", "multi-line"], + "dot-location": ["error", "property"], + "grouped-accessor-pairs": ["error", "getBeforeSet"], + "eqeqeq": ["error", "always"], + "no-case-declarations": "error", + "no-else-return": "error", + "no-fallthrough": "off", + "no-floating-decimal": "error", + "no-multi-spaces": "error", + "no-useless-escape": "error", + + /* Variables */ + "no-shadow": "off", + "no-unused-vars": "off", + "no-use-before-define": "off", + + /* Stylistic Issues */ + "block-spacing": "error", + "brace-style": ["error", "stroustrup"], + "comma-dangle": ["error", "always-multiline"], + "comma-spacing": ["error", { + "before": false, + "after": true + }], + "comma-style": ["error", "last"], + "eol-last": ["error", "always"], + "indent": ["error", 2, { + "SwitchCase": 1 + }], + "key-spacing": ["error", { + "beforeColon": false + }], + "keyword-spacing": ["error", { + "overrides": { + "else": { "before": false, "after": true } + } + }], + "linebreak-style": "off", + "multiline-comment-style": ["error", "starred-block"], + "newline-per-chained-call": ["error", { + "ignoreChainWithDepth": 2 + }], + "no-multiple-empty-lines": ["error", { + "max": 1, + "maxEOF": 0 + }], + "no-trailing-spaces": ["error", { + "ignoreComments": true + }], + "no-whitespace-before-property": "error", + "object-curly-spacing": ["error", "always"], + "object-property-newline": ["error", { + "allowMultiplePropertiesPerLine": false + }], + "padding-line-between-statements": ["error", + { + "blankLine": "always", + "prev": [ + "block-like", + "multiline-block-like", + "multiline-expression" + ], + "next": "*" + }, + { + "blankLine": "always", + "prev": "*", + "next": [ + "block-like", + "multiline-block-like", + "return" + ] + }, + { + "blankLine": "any", + "prev": "case", + "next": "case" + }, + { + "blankLine": "always", + "prev": ["let", "var", "const"], + "next": ["expression", "multiline-expression"] + }, + { + "blankLine": "always", + "prev": ["expression", "multiline-expression"], + "next": ["let", "var", "const"] + }, + { + "blankLine": "always", + "prev": "*", + "next": ["function", "class"] + } + ], + "quotes": ["error", "single"], + "semi": "off", + "semi-spacing": ["error", { + "before": false, + "after": true + }], + "space-before-blocks": "error", + "space-before-function-paren": ["error", "never"], + "space-infix-ops": "error", + "spaced-comment": "error", + "switch-colon-spacing": "error", + + /* ECMAScript 6 */ + "arrow-parens": "error", + "arrow-spacing": "error", + "no-duplicate-imports": "error", + "no-useless-constructor": "off", + "no-var": "error", + "object-shorthand": "error", + "prefer-arrow-callback": "error" + }, + "overrides": [ + { + "files": [ + "./**/*.ts", + "./**/*.d.ts" + ], + "rules": { + // This rule is disabled for reverse indexing in enums + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-var-requires": "error", + + "@typescript-eslint/semi": ["error", "always"], + "@typescript-eslint/no-shadow": "off", + "@typescript-eslint/no-unused-vars": "error", + "@typescript-eslint/no-use-before-define": ["error", { + "functions": false, + "classes": false, + "variables": true + }], + "@typescript-eslint/no-useless-constructor": "error", + "@typescript-eslint/explicit-module-boundary-types": "error", + + "indent": "off", + "@typescript-eslint/indent": ["error", 2] + } + }, + { + "files": [ + "./**/*.js", + "./**/*.cjs", + "./**/*.mjs" + ], + "rules": { + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/semi": "off", + "@typescript-eslint/no-shadow": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/no-useless-constructor": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "no-undef": "off" + } + } + ] +} diff --git a/nise-replay-viewer/osu-parsers/.npmignore b/nise-replay-viewer/osu-parsers/.npmignore new file mode 100644 index 0000000..dacca7a --- /dev/null +++ b/nise-replay-viewer/osu-parsers/.npmignore @@ -0,0 +1,9 @@ +/** +!/lib +!/lib/** +!.eslintignore +!.eslintrc.json +!.npmignore +!LICENSE +!package.json +!README.md diff --git a/nise-replay-viewer/osu-parsers/LICENSE b/nise-replay-viewer/osu-parsers/LICENSE new file mode 100644 index 0000000..07b9795 --- /dev/null +++ b/nise-replay-viewer/osu-parsers/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2023 Kionell + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/nise-replay-viewer/osu-parsers/README.md b/nise-replay-viewer/osu-parsers/README.md new file mode 100644 index 0000000..f05d2cb --- /dev/null +++ b/nise-replay-viewer/osu-parsers/README.md @@ -0,0 +1,243 @@ +# osu-parsers +[![CodeFactor](https://img.shields.io/codefactor/grade/github/kionell/osu-parsers)](https://www.codefactor.io/repository/github/kionell/osu-parsers) +[![License](https://img.shields.io/github/license/kionell/osu-parsers)](https://github.com/kionell/osu-parsers/blob/master/LICENSE) +[![Package](https://img.shields.io/npm/v/osu-parsers)](https://www.npmjs.com/package/osu-parsers) + + +A bundle of decoders/encoders for osu! file formats based on the osu!lazer source code. + +- Written in TypeScript. +- Based on the osu!lazer source code. +- Allows you to parse beatmaps of any osu! game mode. +- Supports beatmap conversion between different game modes. +- Works in browsers. + +## Installation + +Add a new dependency to your project via npm: + +```bash +npm install osu-parsers +``` + +## Dependencies + +This package comes with built-in LZMA codec as it is required for replay processing. +All classes and their typings can be found in [osu-classes](https://github.com/kionell/osu-classes) package which is a peer dependency and must be installed separately. + +## Supported file formats + +- [.osu](https://osu.ppy.sh/wiki/en/Client/File_formats/osu_%28file_format%29) - fully supported (decoding/encoding) +- [.osb](https://osu.ppy.sh/wiki/en/Client/File_formats/osb_%28file_format%29) - fully supported (decoding/encoding) +- [.osr](https://osu.ppy.sh/wiki/en/Client/File_formats/osr_%28file_format%29) - fully supported (decoding/encoding) + +## Beatmap decoding + +Beatmap decoder is used to read `.osu` files and convert them to the objects of plain [Beatmap](https://kionell.github.io/osu-classes/classes/Beatmap.html) type. +Note that plain beatmap objects can't be used to get max combo, star rating and performance as they don't have ruleset specific data. To get correct beatmap data, beatmap objects should be converted using one of the supported [rulesets](https://github.com/kionell/osu-parsers#rulesets). + +There are 4 ways to read data using this decoders: +- via file path - async +- via data buffer - sync +- via string - sync +- via array of split lines - sync + +By default, beatmap decoder will decode both beatmap and storyboard. If you want to decode beatmap without storyboard, you can pass `false` as the second parameter to any of the methods. +There is also a support for partial beatmap decoding which allows you to skip unnecessary sections. + +### Example of beatmap decoding + +```js +import { BeatmapDecoder } from 'osu-parsers' + +const path = 'path/to/your/file.osu'; +const data = 'osu file format v14...'; + +// This is optional and true by default. +const shouldParseSb = true; + +const decoder = new BeatmapDecoder(); +const beatmap1 = await decoder.decodeFromPath(path, shouldParseSb); + +// Partial beatmap decoding without unnecessary sections. +const beatmap2 = decoder.decodeFromString(data, { + parseGeneral: false, + parseEditor: false, + parseMetadata: false, + parseDifficulty: false, + parseEvents: false, + parseTimingPoints: false, + parseHitObjects: false, + parseStoryboard: false, + parseColours: false, +}); + +console.log(beatmap1) // Beatmap object. +console.log(beatmap2) // Another Beatmap object. +``` + +## Storyboard decoding + +Storyboard decoder is used to read both `.osu` and `.osb` files and convert them to the [Storyboard](https://kionell.github.io/osu-classes/classes/Storyboard.html) objects. + +As in beatmap decoder, there are 4 ways to decode your `.osu` and `.osb` files: +- via file path - async +- via data buffer - sync +- via string - sync +- via array of split lines - sync + +### Example of storyboard decoding + +```js +import { StoryboardDecoder } from 'osu-parsers' + +const pathToOsb = 'path/to/your/file.osb'; +const pathToOsu = 'path/to/your/file.osu'; + +const decoder = new StoryboardDecoder(); + +// Parse a single storyboard file (from .osb) for beatmaps that doesn't have internal storyboard (from .osu) +const storyboard1 = await decoder.decodeFromPath(pathToOsb); + +// Combines main storyboard (from .osu) with secondary storyboard (from .osb) +const storyboard2 = await decoder.decodeFromPath(pathToOsu, pathToOsb); + +console.log(storyboard1); // Storyboard object. +console.log(storyboard2); // Another Storyboard object. +``` + +## Score & replay decoding + +Score decoder is used to decode `.osr` files and convert them to the [Score](https://kionell.github.io/osu-classes/classes/Score.html) objects. +Score object contains score information and replay data of plain [Replay](https://kionell.github.io/osu-classes/classes/Replay.html) type. +Note that all `.osr` files contain raw legacy frame data that was initially intended for osu!standard only. To get correct data, replay objects should be converted using one of the supported [rulesets](https://github.com/kionell/osu-parsers#rulesets). +This decoder is based on external LZMA library and works asynchronously. + +There are 2 ways to read data through this decoder: +- via file path - async +- via buffer - async + +By default, score decoder will decode both score information and replay. If you want to decode score information without replay, you can pass `false` as the second parameter to any of the methods. + +### Example of score & replay decoding + +```js +import { ScoreDecoder } from 'osu-parsers' + +const path = 'path/to/your/file.osr'; + +// This is optional and true by default. +const parseReplay = true; + +const decoder = new ScoreDecoder(); + +const score = await decoder.decodeFromPath(path, parseReplay) + +console.log(score.info); // ScoreInfo object. +console.log(score.replay); // Replay object or null. +``` + +## Encoding + +All objects parsed by these decoders can easily be stringified and written to the files. +Note that encoders will write object data without any changes. For example, if you try to encode beatmap with applied mods, it will write modded values! + +When encoding is complete you can import resulting files to the game. + +### Example of beatmap encoding + +```js +import { BeatmapDecoder, BeatmapEncoder } from 'osu-parsers' + +const decodePath = 'path/to/your/file.osu'; +const shouldParseSb = true; + +const decoder = new BeatmapDecoder(); +const encoder = new BeatmapEncoder(); + +const beatmap = await decoder.decodeFromPath(decodePath, shouldParseSb); + +// Do your stuff with beatmap... + +const encodePath = 'path/to/your/file.osu'; + +// Write IBeatmap object to an .osu file. +await encoder.encodeToPath(encodePath, beatmap); + +// You can also encode IBeatmap object to a string. +const stringified = encoder.encodeToString(beatmap); +``` + +### Example of storyboard encoding + +```js +import { StoryboardDecoder, StoryboardEncoder } from 'osu-parsers' + +const decodePath = 'path/to/your/file.osb'; + +const decoder = new StoryboardDecoder(); +const encoder = new StoryboardEncoder(); + +const storyboard = await decoder.decodeFromPath(decodePath); + +// Do your stuff with storyboard... + +const encodePath = 'path/to/your/file.osb'; + +// Write Storyboard object to an .osb file. +await encoder.encodeToPath(encodePath, storyboard); + +// You can also encode Storyboard object to a string. +const stringified = encoder.encodeToString(storyboard); +``` + +### Example of score & replay encoding + +```js +import { ScoreDecoder, ScoreEncoder } from 'osu-parsers' + +const decodePath = 'path/to/your/file.osr'; + +const decoder = new ScoreDecoder(); +const encoder = new ScoreEncoder(); + +const score = await decoder.decodeFromPath(decodePath); + +// Do your stuff with score info and replay... + +const encodePath = 'path/to/your/file.osr'; + +// Write IScore object to an .osr file. +await encoder.encodeToPath(encodePath, score); + +// You can also encode IScore object to a buffer. +const buffer = await encoder.encodeToBuffer(score); +``` + +## Rulesets + +This library by itself doesn't provide any tools for difficulty and performance calculation!!!! +If you are looking for something related to this, then rulesets may come in handy for you. +Rulesets are separate libraries based on the classes from the [osu-classes](https://github.com/kionell/osu-classes.git) project. They allow you to work with gamemode specific stuff as difficulty, performance, mods and max combo calculation. Because of the shared logic between all of the rulesets they are compatible between each other. If you want, you can even write your own custom ruleset! +The great thing about all this stuff is a beatmap and replay conversion. Any beatmap or replay can be used with any ruleset library as long as they implement the same interfaces. + +There are 4 basic rulesets that support parsed beatmaps from this decoder: + +- [osu-standard-stable](https://github.com/kionell/osu-standard-stable.git) - The osu!std ruleset based on the osu!lazer source code. +- [osu-taiko-stable](https://github.com/kionell/osu-taiko-stable.git) - The osu!taiko ruleset based on the osu!lazer source code. +- [osu-catch-stable](https://github.com/kionell/osu-catch-stable.git) - The osu!catch ruleset based on the osu!lazer source code. +- [osu-mania-stable](https://github.com/kionell/osu-mania-stable.git) - The osu!mania ruleset based on the osu!lazer source code. + +You can also try existing [osu-pp-calculator](https://github.com/kionell/osu-pp-calculator) package. It's a wrapper for all 4 rulesets above with a lot of extra useful stuff like score simulation and beatmap downloading. + +## Documentation + +Auto-generated documentation is available [here](https://kionell.github.io/osu-parsers/). + +## Contributing + +This project is being developed personally by me on pure enthusiasm. If you want to help with development or fix a problem, then feel free to create a new pull request. For major changes, please open an issue first to discuss what you would like to change. + +## License + +This project is licensed under the MIT License - see the [LICENSE](https://choosealicense.com/licenses/mit/) for details. diff --git a/nise-replay-viewer/osu-parsers/lib/browser.mjs b/nise-replay-viewer/osu-parsers/lib/browser.mjs new file mode 100644 index 0000000..644c54b --- /dev/null +++ b/nise-replay-viewer/osu-parsers/lib/browser.mjs @@ -0,0 +1,3282 @@ +import { + Color4, + EventType, + StoryboardSample, + StoryboardAnimation, + Vector2, + StoryboardSprite, + LayerType, + StoryboardVideo, + CompoundType, + CommandType, + ParameterType, + BlendingParameters, + Origins, + Anchor, + LoopType, + BeatmapBreakEvent, + SampleSet, + HitObject, + SliderPath, + CommandLoop, + CommandTrigger, + HitType, + HitSound, + PathType, + EffectType, + TimeSignature, + ControlPointType, + ReplayButtonState, + LegacyReplayFrame, + Storyboard, + SampleBank, + PathPoint, + HitSample, + TimingPoint, + DifficultyPoint, + EffectPoint, + SamplePoint, + LifeBarFrame, + Beatmap, + ScoreInfo, + Replay, + Score, +} from "osu-classes"; +import { decompress, compress } from "lzma-js-simple-v2"; + +class Parsing { + static parseInt(input, parseLimit = this.MAX_PARSE_VALUE, allowNaN = false) { + return this._getValue(parseInt(input), parseLimit, allowNaN); + } + static parseFloat( + input, + parseLimit = this.MAX_PARSE_VALUE, + allowNaN = false + ) { + return this._getValue(parseFloat(input), parseLimit, allowNaN); + } + static parseEnum(enumObj, input) { + const value = input.trim(); + const rawValue = parseInt(value); + + if (rawValue in enumObj) { + return rawValue; + } + + if (value in enumObj) { + return enumObj[value]; + } + + throw new Error("Unknown enum value!"); + } + static parseByte(input) { + const value = parseInt(input); + + if (value < 0) { + throw new Error("Value must be greater than 0!"); + } + + if (value > 255) { + throw new Error("Value must be less than 255!"); + } + + return this._getValue(value); + } + static _getValue(value, parseLimit = this.MAX_PARSE_VALUE, allowNaN = false) { + if (value < -parseLimit) { + throw new Error("Value is too low!"); + } + + if (value > parseLimit) { + throw new Error("Value is too high!"); + } + + if (!allowNaN && Number.isNaN(value)) { + throw new Error("Not a number"); + } + + return value; + } +} +Parsing.MAX_COORDINATE_VALUE = 131072; +Parsing.MAX_PARSE_VALUE = 2147483647; + +class BeatmapColorDecoder { + static handleLine(line, output) { + const [key, ...values] = line.split(":"); + const rgba = values + .join(":") + .trim() + .split(",") + .map((c) => Parsing.parseByte(c)); + + if (rgba.length !== 3 && rgba.length !== 4) { + throw new Error( + `Color specified in incorrect format (should be R,G,B or R,G,B,A): ${rgba.join( + "," + )}` + ); + } + + const color = new Color4(rgba[0], rgba[1], rgba[2], rgba[3]); + + this.addColor(color, output, key.trim()); + } + static addColor(color, output, key) { + if (key === "SliderTrackOverride") { + output.colors.sliderTrackColor = color; + + return; + } + + if (key === "SliderBorder") { + output.colors.sliderBorderColor = color; + + return; + } + + if (key.startsWith("Combo")) { + output.colors.comboColors.push(color); + } + } +} + +class BeatmapDifficultyDecoder { + static handleLine(line, beatmap) { + const [key, ...values] = line.split(":"); + const value = values.join(":").trim(); + + switch (key.trim()) { + case "CircleSize": + beatmap.difficulty.circleSize = Parsing.parseFloat(value); + break; + case "HPDrainRate": + beatmap.difficulty.drainRate = Parsing.parseFloat(value); + break; + case "OverallDifficulty": + beatmap.difficulty.overallDifficulty = Parsing.parseFloat(value); + break; + case "ApproachRate": + beatmap.difficulty.approachRate = Parsing.parseFloat(value); + break; + case "SliderMultiplier": + beatmap.difficulty.sliderMultiplier = Parsing.parseFloat(value); + break; + case "SliderTickRate": + beatmap.difficulty.sliderTickRate = Parsing.parseFloat(value); + } + } +} + +class BeatmapEditorDecoder { + static handleLine(line, beatmap) { + const [key, ...values] = line.split(":"); + const value = values.join(":").trim(); + + switch (key.trim()) { + case "Bookmarks": + beatmap.editor.bookmarks = value.split(",").map((v) => +v); + break; + case "DistanceSpacing": + beatmap.editor.distanceSpacing = Math.max(0, Parsing.parseFloat(value)); + break; + case "BeatDivisor": + beatmap.editor.beatDivisor = Parsing.parseInt(value); + break; + case "GridSize": + beatmap.editor.gridSize = Parsing.parseInt(value); + break; + case "TimelineZoom": + beatmap.editor.timelineZoom = Math.max(0, Parsing.parseFloat(value)); + } + } +} + +class StoryboardEventDecoder { + static handleLine(line, storyboard) { + const depth = this._getDepth(line); + + if (depth > 0) { + line = line.substring(depth); + } + + if (depth < 2 && this._storyboardSprite) { + this._timelineGroup = this._storyboardSprite.timelineGroup; + } + + switch (depth) { + case 0: + return this._handleElement(line, storyboard); + case 1: + return this._handleCompoundOrCommand(line); + case 2: + return this._handleCommand(line); + } + } + static _handleElement(line, storyboard) { + let _a; + const data = line.split(","); + const eventType = this.parseEventType(data[0]); + + if ( + (_a = this._storyboardSprite) === null || _a === void 0 + ? void 0 + : _a.hasCommands + ) { + this._storyboardSprite.updateCommands(); + this._storyboardSprite.adjustTimesToCommands(); + this._storyboardSprite.resetValuesToCommands(); + } + + switch (eventType) { + case EventType.Video: { + const layer = storyboard.getLayerByType(LayerType.Video); + const offset = Parsing.parseInt(data[1]); + const path = data[2].replace(/"/g, ""); + + layer.elements.push(new StoryboardVideo(path, offset)); + + return; + } + case EventType.Sprite: { + const layer = storyboard.getLayerByType(this.parseLayerType(data[1])); + const origin = this.parseOrigin(data[2]); + const anchor = this.convertOrigin(origin); + const path = data[3].replace(/"/g, ""); + const x = Parsing.parseFloat(data[4], Parsing.MAX_COORDINATE_VALUE); + const y = Parsing.parseFloat(data[5], Parsing.MAX_COORDINATE_VALUE); + + this._storyboardSprite = new StoryboardSprite( + path, + origin, + anchor, + new Vector2(x, y) + ); + layer.elements.push(this._storyboardSprite); + + return; + } + case EventType.Animation: { + const layer = storyboard.getLayerByType(this.parseLayerType(data[1])); + const origin = this.parseOrigin(data[2]); + const anchor = this.convertOrigin(origin); + const path = data[3].replace(/"/g, ""); + const x = Parsing.parseFloat(data[4], Parsing.MAX_COORDINATE_VALUE); + const y = Parsing.parseFloat(data[5], Parsing.MAX_COORDINATE_VALUE); + const frameCount = Parsing.parseInt(data[6]); + let frameDelay = Parsing.parseFloat(data[7]); + + if (storyboard.fileFormat < 6) { + frameDelay = Math.round(0.015 * frameDelay) * 1.186 * (1000 / 60); + } + + const loopType = this.parseLoopType(data[8]); + + this._storyboardSprite = new StoryboardAnimation( + path, + origin, + anchor, + new Vector2(x, y), + frameCount, + frameDelay, + loopType + ); + layer.elements.push(this._storyboardSprite); + + return; + } + case EventType.Sample: { + const time = Parsing.parseFloat(data[1]); + const layer = storyboard.getLayerByType(this.parseLayerType(data[2])); + const path = data[3].replace(/"/g, ""); + const volume = data.length > 4 ? Parsing.parseInt(data[4]) : 100; + const sample = new StoryboardSample(path, time, volume); + + layer.elements.push(sample); + } + } + } + static _handleCompoundOrCommand(line) { + let _a, _b; + const data = line.split(","); + const compoundType = data[0]; + + switch (compoundType) { + case CompoundType.Trigger: { + this._timelineGroup = + (_a = this._storyboardSprite) === null || _a === void 0 + ? void 0 + : _a.addTrigger( + data[1], + data.length > 2 ? Parsing.parseFloat(data[2]) : -Infinity, + data.length > 3 ? Parsing.parseFloat(data[3]) : Infinity, + data.length > 4 ? Parsing.parseInt(data[4]) : 0 + ); + + return; + } + case CompoundType.Loop: { + this._timelineGroup = + (_b = this._storyboardSprite) === null || _b === void 0 + ? void 0 + : _b.addLoop( + Parsing.parseFloat(data[1]), + Math.max(0, Parsing.parseInt(data[2]) - 1) + ); + + return; + } + + default: + this._handleCommand(line); + } + } + static _handleCommand(line) { + let _a, _b, _c, _d, _e, _f, _g, _h, _j; + const data = line.split(","); + const type = data[0]; + const easing = Parsing.parseInt(data[1]); + const startTime = Parsing.parseInt(data[2]); + const endTime = Parsing.parseInt(data[3] || data[2]); + + switch (type) { + case CommandType.Fade: { + const startValue = Parsing.parseFloat(data[4]); + const endValue = + data.length > 5 ? Parsing.parseFloat(data[5]) : startValue; + + (_a = this._timelineGroup) === null || _a === void 0 + ? void 0 + : _a.alpha.add( + type, + easing, + startTime, + endTime, + startValue, + endValue + ); + + return; + } + case CommandType.Scale: { + const startValue = Parsing.parseFloat(data[4]); + const endValue = + data.length > 5 ? Parsing.parseFloat(data[5]) : startValue; + + (_b = this._timelineGroup) === null || _b === void 0 + ? void 0 + : _b.scale.add( + type, + easing, + startTime, + endTime, + startValue, + endValue + ); + + return; + } + case CommandType.VectorScale: { + const startX = Parsing.parseFloat(data[4]); + const startY = Parsing.parseFloat(data[5]); + const endX = data.length > 6 ? Parsing.parseFloat(data[6]) : startX; + const endY = data.length > 7 ? Parsing.parseFloat(data[7]) : startY; + + (_c = this._timelineGroup) === null || _c === void 0 + ? void 0 + : _c.vectorScale.add( + type, + easing, + startTime, + endTime, + new Vector2(startX, startY), + new Vector2(endX, endY) + ); + + return; + } + case CommandType.Rotation: { + const startValue = Parsing.parseFloat(data[4]); + const endValue = + data.length > 5 ? Parsing.parseFloat(data[5]) : startValue; + + (_d = this._timelineGroup) === null || _d === void 0 + ? void 0 + : _d.rotation.add( + type, + easing, + startTime, + endTime, + startValue, + endValue + ); + + return; + } + case CommandType.Movement: { + const startX = Parsing.parseFloat(data[4]); + const startY = Parsing.parseFloat(data[5]); + const endX = data.length > 6 ? Parsing.parseFloat(data[6]) : startX; + const endY = data.length > 7 ? Parsing.parseFloat(data[7]) : startY; + + (_e = this._timelineGroup) === null || _e === void 0 + ? void 0 + : _e.x.add( + CommandType.MovementX, + easing, + startTime, + endTime, + startX, + endX + ); + (_f = this._timelineGroup) === null || _f === void 0 + ? void 0 + : _f.y.add( + CommandType.MovementY, + easing, + startTime, + endTime, + startY, + endY + ); + + return; + } + case CommandType.MovementX: { + const startValue = Parsing.parseFloat(data[4]); + const endValue = + data.length > 5 ? Parsing.parseFloat(data[5]) : startValue; + + (_g = this._timelineGroup) === null || _g === void 0 + ? void 0 + : _g.x.add(type, easing, startTime, endTime, startValue, endValue); + + return; + } + case CommandType.MovementY: { + const startValue = Parsing.parseFloat(data[4]); + const endValue = + data.length > 5 ? Parsing.parseFloat(data[5]) : startValue; + + (_h = this._timelineGroup) === null || _h === void 0 + ? void 0 + : _h.y.add(type, easing, startTime, endTime, startValue, endValue); + + return; + } + case CommandType.Color: { + const startRed = Parsing.parseFloat(data[4]); + const startGreen = Parsing.parseFloat(data[5]); + const startBlue = Parsing.parseFloat(data[6]); + const endRed = data.length > 7 ? Parsing.parseFloat(data[7]) : startRed; + const endGreen = + data.length > 8 ? Parsing.parseFloat(data[8]) : startGreen; + const endBlue = + data.length > 9 ? Parsing.parseFloat(data[9]) : startBlue; + + (_j = this._timelineGroup) === null || _j === void 0 + ? void 0 + : _j.color.add( + type, + easing, + startTime, + endTime, + new Color4(startRed, startGreen, startBlue, 1), + new Color4(endRed, endGreen, endBlue, 1) + ); + + return; + } + case CommandType.Parameter: { + return this._handleParameterCommand(data); + } + } + + throw new Error(`Unknown command type: ${type}`); + } + static _handleParameterCommand(data) { + let _a, _b, _c; + const type = CommandType.Parameter; + const easing = Parsing.parseInt(data[1]); + const startTime = Parsing.parseInt(data[2]); + const endTime = Parsing.parseInt(data[3] || data[2]); + const parameter = data[4]; + + switch (parameter) { + case ParameterType.BlendingMode: { + const startValue = BlendingParameters.Additive; + const endValue = + startTime === endTime + ? BlendingParameters.Additive + : BlendingParameters.Inherit; + + (_a = this._timelineGroup) === null || _a === void 0 + ? void 0 + : _a.blendingParameters.add( + type, + easing, + startTime, + endTime, + startValue, + endValue, + parameter + ); + + return; + } + case ParameterType.HorizontalFlip: + (_b = this._timelineGroup) === null || _b === void 0 + ? void 0 + : _b.flipH.add( + type, + easing, + startTime, + endTime, + true, + startTime === endTime, + parameter + ); + + return; + case ParameterType.VerticalFlip: { + (_c = this._timelineGroup) === null || _c === void 0 + ? void 0 + : _c.flipV.add( + type, + easing, + startTime, + endTime, + true, + startTime === endTime, + parameter + ); + + return; + } + } + + throw new Error(`Unknown parameter type: ${parameter}`); + } + static parseEventType(input) { + if (input.startsWith(" ") || input.startsWith("_")) { + return EventType.StoryboardCommand; + } + + try { + return Parsing.parseEnum(EventType, input); + } catch { + throw new Error(`Unknown event type: ${input}`); + } + } + static parseLayerType(input) { + try { + return Parsing.parseEnum(LayerType, input); + } catch { + throw new Error(`Unknown layer type: ${input}`); + } + } + static parseOrigin(input) { + try { + return Parsing.parseEnum(Origins, input); + } catch { + return Origins.TopLeft; + } + } + static convertOrigin(origin) { + switch (origin) { + case Origins.TopLeft: + return Anchor.TopLeft; + case Origins.TopCentre: + return Anchor.TopCentre; + case Origins.TopRight: + return Anchor.TopRight; + case Origins.CentreLeft: + return Anchor.CentreLeft; + case Origins.Centre: + return Anchor.Centre; + case Origins.CentreRight: + return Anchor.CentreRight; + case Origins.BottomLeft: + return Anchor.BottomLeft; + case Origins.BottomCentre: + return Anchor.BottomCentre; + case Origins.BottomRight: + return Anchor.BottomRight; + } + + return Anchor.TopLeft; + } + static parseLoopType(input) { + try { + return Parsing.parseEnum(LoopType, input); + } catch { + return LoopType.LoopForever; + } + } + static _getDepth(line) { + let depth = 0; + + for (const char of line) { + if (char !== " " && char !== "_") { + break; + } + + ++depth; + } + + return depth; + } +} + +class BeatmapEventDecoder { + static handleLine(line, beatmap, sbLines, offset) { + const data = line.split(",").map((v, i) => (i ? v.trim() : v)); + const eventType = StoryboardEventDecoder.parseEventType(data[0]); + + switch (eventType) { + case EventType.Background: + beatmap.events.backgroundPath = data[2].replace(/"/g, ""); + break; + case EventType.Break: { + const start = Parsing.parseFloat(data[1]) + offset; + const end = Math.max(start, Parsing.parseFloat(data[2]) + offset); + const breakEvent = new BeatmapBreakEvent(start, end); + + if (!beatmap.events.breaks) { + beatmap.events.breaks = []; + } + + beatmap.events.breaks.push(breakEvent); + break; + } + case EventType.Video: + case EventType.Sample: + case EventType.Sprite: + case EventType.Animation: + case EventType.StoryboardCommand: + if (sbLines) { + sbLines.push(line); + } + } + } +} + +class BeatmapGeneralDecoder { + static handleLine(line, beatmap, offset) { + const [key, ...values] = line.split(":"); + const value = values.join(":").trim(); + + switch (key.trim()) { + case "AudioFilename": + beatmap.general.audioFilename = value; + break; + case "AudioHash": + beatmap.general.audioHash = value; + break; + case "OverlayPosition": + beatmap.general.overlayPosition = value; + break; + case "SkinPreference": + beatmap.general.skinPreference = value; + break; + case "AudioLeadIn": + beatmap.general.audioLeadIn = Parsing.parseInt(value); + break; + case "PreviewTime": + beatmap.general.previewTime = Parsing.parseInt(value) + offset; + break; + case "Countdown": + beatmap.general.countdown = Parsing.parseInt(value); + break; + case "StackLeniency": + beatmap.general.stackLeniency = Parsing.parseFloat(value); + break; + case "Mode": + beatmap.originalMode = Parsing.parseInt(value); + break; + case "CountdownOffset": + beatmap.general.countdownOffset = Parsing.parseInt(value); + break; + case "SampleSet": + beatmap.general.sampleSet = SampleSet[value]; + break; + case "LetterboxInBreaks": + beatmap.general.letterboxInBreaks = value === "1"; + break; + case "StoryFireInFront": + beatmap.general.storyFireInFront = value === "1"; + break; + case "UseSkinSprites": + beatmap.general.useSkinSprites = value === "1"; + break; + case "AlwaysShowPlayfield": + beatmap.general.alwaysShowPlayfield = value === "1"; + break; + case "EpilepsyWarning": + beatmap.general.epilepsyWarning = value === "1"; + break; + case "SpecialStyle": + beatmap.general.specialStyle = value === "1"; + break; + case "WidescreenStoryboard": + beatmap.general.widescreenStoryboard = value === "1"; + break; + case "SamplesMatchPlaybackRate": + beatmap.general.samplesMatchPlaybackRate = value === "1"; + } + } +} + +class HittableObject extends HitObject { + constructor() { + super(...arguments); + this.isNewCombo = false; + this.comboOffset = 0; + } + clone() { + const cloned = super.clone(); + + cloned.isNewCombo = this.isNewCombo; + cloned.comboOffset = this.comboOffset; + + return cloned; + } +} + +class HoldableObject extends HitObject { + constructor() { + super(...arguments); + this.endTime = 0; + this.nodeSamples = []; + } + get duration() { + return this.endTime - this.startTime; + } + clone() { + const cloned = super.clone(); + + cloned.endTime = this.endTime; + cloned.nestedHitObjects = this.nestedHitObjects.map((h) => h.clone()); + cloned.nodeSamples = this.nodeSamples.map((n) => n.map((s) => s.clone())); + + return cloned; + } +} + +class SlidableObject extends HitObject { + constructor() { + super(...arguments); + this.repeats = 0; + this.velocity = 1; + this.path = new SliderPath(); + this.legacyLastTickOffset = 36; + this.nodeSamples = []; + this.isNewCombo = false; + this.comboOffset = 0; + } + get duration() { + return this.spans * this.spanDuration; + } + get endTime() { + return this.startTime + this.duration; + } + get spans() { + return this.repeats + 1; + } + set spans(value) { + this.repeats = value - 1; + } + get spanDuration() { + return this.distance / this.velocity; + } + get distance() { + return this.path.distance; + } + set distance(value) { + this.path.distance = value; + } + applyDefaultsToSelf(controlPoints, difficulty) { + super.applyDefaultsToSelf(controlPoints, difficulty); + + const timingPoint = controlPoints.timingPointAt(this.startTime); + const difficultyPoint = controlPoints.difficultyPointAt(this.startTime); + const scoringDistance = + SlidableObject.BASE_SCORING_DISTANCE * + difficulty.sliderMultiplier * + difficultyPoint.sliderVelocity; + + this.velocity = scoringDistance / timingPoint.beatLength; + } + clone() { + const cloned = super.clone(); + + cloned.legacyLastTickOffset = this.legacyLastTickOffset; + cloned.nodeSamples = this.nodeSamples.map((n) => n.map((s) => s.clone())); + cloned.velocity = this.velocity; + cloned.repeats = this.repeats; + cloned.path = this.path.clone(); + cloned.isNewCombo = this.isNewCombo; + cloned.comboOffset = this.comboOffset; + + return cloned; + } +} +SlidableObject.BASE_SCORING_DISTANCE = 100; + +class SpinnableObject extends HitObject { + constructor() { + super(...arguments); + this.endTime = 0; + this.isNewCombo = false; + this.comboOffset = 0; + } + get duration() { + return this.endTime - this.startTime; + } + clone() { + const cloned = super.clone(); + + cloned.endTime = this.endTime; + cloned.isNewCombo = this.isNewCombo; + cloned.comboOffset = this.comboOffset; + + return cloned; + } +} + +let FileFormat; + +(function (FileFormat) { + FileFormat["Beatmap"] = ".osu"; + FileFormat["Storyboard"] = ".osb"; + FileFormat["Replay"] = ".osr"; +})(FileFormat || (FileFormat = {})); + +let LineType; + +(function (LineType) { + LineType[(LineType["FileFormat"] = 0)] = "FileFormat"; + LineType[(LineType["Section"] = 1)] = "Section"; + LineType[(LineType["Empty"] = 2)] = "Empty"; + LineType[(LineType["Data"] = 3)] = "Data"; + LineType[(LineType["Break"] = 4)] = "Break"; +})(LineType || (LineType = {})); + +let Section; + +(function (Section) { + Section["General"] = "General"; + Section["Editor"] = "Editor"; + Section["Metadata"] = "Metadata"; + Section["Difficulty"] = "Difficulty"; + Section["Events"] = "Events"; + Section["TimingPoints"] = "TimingPoints"; + Section["Colours"] = "Colours"; + Section["HitObjects"] = "HitObjects"; + Section["Variables"] = "Variables"; + Section["Fonts"] = "Fonts"; + Section["CatchTheBeat"] = "CatchTheBeat"; + Section["Mania"] = "Mania"; +})(Section || (Section = {})); + +const browserFSOperation = function () { + throw new Error( + "Filesystem operations are not available in a browser environment" + ); +}; + +class BeatmapColorEncoder { + static encodeColors(beatmap) { + const colors = beatmap.colors; + + if (Object.keys(colors).length === 1 && !colors.comboColors.length) { + return ""; + } + + const encoded = ["[Colours]"]; + + colors.comboColors.forEach((color, i) => { + encoded.push(`Combo${i + 1}:${color}`); + }); + + if (colors.sliderTrackColor) { + encoded.push(`SliderTrackOverride:${colors.sliderTrackColor}`); + } + + if (colors.sliderBorderColor) { + encoded.push(`SliderBorder:${colors.sliderBorderColor}`); + } + + return encoded.join("\n"); + } +} + +class BeatmapDifficultyEncoder { + static encodeDifficultySection(beatmap) { + const encoded = ["[Difficulty]"]; + const difficulty = beatmap.difficulty; + + encoded.push(`HPDrainRate:${difficulty.drainRate}`); + encoded.push(`CircleSize:${difficulty.circleSize}`); + encoded.push(`OverallDifficulty:${difficulty.overallDifficulty}`); + encoded.push(`ApproachRate:${difficulty.approachRate}`); + encoded.push(`SliderMultiplier:${difficulty.sliderMultiplier}`); + encoded.push(`SliderTickRate:${difficulty.sliderTickRate}`); + + return encoded.join("\n"); + } +} + +class BeatmapEditorEncoder { + static encodeEditorSection(beatmap) { + const encoded = ["[Editor]"]; + const editor = beatmap.editor; + + encoded.push(`Bookmarks:${editor.bookmarks.join(",")}`); + encoded.push(`DistanceSpacing:${editor.distanceSpacing}`); + encoded.push(`BeatDivisor:${editor.beatDivisor}`); + encoded.push(`GridSize:${editor.gridSize}`); + encoded.push(`TimelineZoom:${editor.timelineZoom}`); + + return encoded.join("\n"); + } +} + +class StoryboardEventEncoder { + static encodeEventSection(storyboard) { + const encoded = []; + + encoded.push("[Events]"); + encoded.push("//Background and Video events"); + encoded.push(this.encodeVideos(storyboard)); + encoded.push(this.encodeStoryboard(storyboard)); + + return encoded.join("\n"); + } + static encodeVideos(storyboard) { + const encoded = []; + const video = storyboard.getLayerByType(LayerType.Video); + + if (video.elements.length > 0) { + encoded.push(this.encodeStoryboardLayer(video)); + } + + return encoded.join("\n"); + } + static encodeStoryboard(storyboard) { + const encoded = []; + + encoded.push("//Storyboard Layer 0 (Background)"); + + const background = storyboard.getLayerByType(LayerType.Background); + + if (background.elements.length > 0) { + encoded.push(this.encodeStoryboardLayer(background)); + } + + encoded.push("//Storyboard Layer 1 (Fail)"); + + const fail = storyboard.getLayerByType(LayerType.Fail); + + if (fail.elements.length > 0) { + encoded.push(this.encodeStoryboardLayer(fail)); + } + + encoded.push("//Storyboard Layer 2 (Pass)"); + + const pass = storyboard.getLayerByType(LayerType.Pass); + + if (pass.elements.length > 0) { + encoded.push(this.encodeStoryboardLayer(pass)); + } + + encoded.push("//Storyboard Layer 3 (Foreground)"); + + const foreground = storyboard.getLayerByType(LayerType.Foreground); + + if (foreground.elements.length > 0) { + encoded.push(this.encodeStoryboardLayer(foreground)); + } + + encoded.push("//Storyboard Layer 4 (Overlay)"); + + const overlay = storyboard.getLayerByType(LayerType.Overlay); + + if (overlay.elements.length > 0) { + encoded.push(this.encodeStoryboardLayer(overlay)); + } + + return encoded.join("\n"); + } + static encodeStoryboardLayer(layer) { + const encoded = []; + + layer.elements.forEach((element) => { + let _a, _b, _c; + + encoded.push(this.encodeStoryboardElement(element, layer.name)); + + const elementWithCommands = element; + + (_a = + elementWithCommands === null || elementWithCommands === void 0 + ? void 0 + : elementWithCommands.loops) === null || _a === void 0 + ? void 0 + : _a.forEach((loop) => { + if (loop.commands.length > 0) { + encoded.push(this.encodeCompound(loop)); + encoded.push(this.encodeTimelineGroup(loop, 2)); + } + }); + + if ( + ((_b = + elementWithCommands === null || elementWithCommands === void 0 + ? void 0 + : elementWithCommands.timelineGroup) === null || _b === void 0 + ? void 0 + : _b.commands.length) > 0 + ) { + encoded.push( + this.encodeTimelineGroup(elementWithCommands.timelineGroup) + ); + } + + (_c = + elementWithCommands === null || elementWithCommands === void 0 + ? void 0 + : elementWithCommands.triggers) === null || _c === void 0 + ? void 0 + : _c.forEach((trigger) => { + if (trigger.commands.length > 0) { + encoded.push(this.encodeCompound(trigger)); + encoded.push(this.encodeTimelineGroup(trigger, 2)); + } + }); + }); + + return encoded.join("\n"); + } + static encodeStoryboardElement(element, layer) { + if (element instanceof StoryboardAnimation) { + return [ + "Animation", + layer, + Origins[element.origin], + `"${element.filePath}"`, + element.startPosition, + element.frameCount, + element.frameDelay, + element.loopType, + ].join(","); + } + + if (element instanceof StoryboardSprite) { + return [ + "Sprite", + layer, + Origins[element.origin], + `"${element.filePath}"`, + element.startPosition, + ].join(","); + } + + if (element instanceof StoryboardSample) { + return [ + "Sample", + element.startTime, + layer, + `"${element.filePath}"`, + element.volume, + ].join(","); + } + + if (element instanceof StoryboardVideo) { + return ["Video", element.startTime, element.filePath, "0,0"].join(","); + } + + return ""; + } + static encodeCompound(compound, depth = 1) { + const indentation = "".padStart(depth, " "); + + if (compound instanceof CommandLoop) { + return ( + indentation + + [compound.type, compound.loopStartTime, compound.totalIterations].join( + "," + ) + ); + } + + if (compound instanceof CommandTrigger) { + return ( + indentation + + [ + compound.type, + compound.triggerName, + compound.triggerStartTime, + compound.triggerEndTime, + compound.groupNumber, + ].join(",") + ); + } + + return ""; + } + static encodeTimelineGroup(timelineGroup, depth = 1) { + const indentation = "".padStart(depth, " "); + const encoded = []; + const commands = timelineGroup.commands; + let shouldSkip = false; + + for (let i = 0; i < commands.length; ++i) { + if (shouldSkip) { + shouldSkip = false; + continue; + } + + if (i < commands.length - 1) { + const current = commands[i]; + const next = commands[i + 1]; + const currentMoveX = current.type === CommandType.MovementX; + const nextMoveY = next.type === CommandType.MovementY; + const sameEasing = current.easing === next.easing; + const sameStartTime = current.startTime === next.startTime; + const sameEndTime = current.endTime === next.endTime; + const sameCommand = sameEasing && sameStartTime && sameEndTime; + + if (currentMoveX && nextMoveY && sameCommand) { + encoded.push(indentation + this.encodeMoveCommand(current, next)); + shouldSkip = true; + continue; + } + } + + encoded.push(indentation + this.encodeCommand(commands[i])); + } + + return encoded.join("\n"); + } + static encodeMoveCommand(moveX, moveY) { + const encoded = [ + CommandType.Movement, + moveX.easing, + moveX.startTime, + moveX.startTime !== moveX.endTime ? moveX.endTime : "", + moveX.startValue, + moveY.startValue, + ]; + const equalX = moveX.startValue === moveX.endValue; + const equalY = moveY.startValue === moveY.endValue; + + if (!equalX || !equalY) { + encoded.push(`${moveX.endValue},${moveY.endValue}`); + } + + return encoded.join(","); + } + static encodeCommand(command) { + const encoded = [ + command.type, + command.easing, + command.startTime, + command.startTime !== command.endTime ? command.endTime : "", + this._encodeCommandParams(command), + ]; + + return encoded.join(","); + } + static _encodeCommandParams(command) { + if (command.type === CommandType.Parameter) { + return command.parameter; + } + + if (command.type === CommandType.Color) { + const toRGB = (c) => `${c.red},${c.green},${c.blue}`; + const colorCommand = command; + const start = colorCommand.startValue; + const end = colorCommand.endValue; + + return this._areValuesEqual(command) + ? toRGB(start) + : toRGB(start) + "," + toRGB(end); + } + + return this._areValuesEqual(command) + ? `${command.startValue}` + : `${command.startValue},${command.endValue}`; + } + static _areValuesEqual(command) { + if (command.type === CommandType.VectorScale) { + const vectorCommand = command; + + return vectorCommand.startValue.equals(vectorCommand.endValue); + } + + if (command.type === CommandType.Color) { + const colorCommand = command; + + return colorCommand.startValue.equals(colorCommand.endValue); + } + + return command.startValue === command.endValue; + } +} + +class BeatmapEventEncoder { + static encodeEventSection(beatmap) { + const encoded = []; + const events = beatmap.events; + + encoded.push("[Events]"); + encoded.push("//Background and Video events"); + + if (events.backgroundPath) { + encoded.push(`0,0,"${events.backgroundPath}",0,0`); + } + + if (events.storyboard) { + encoded.push(StoryboardEventEncoder.encodeVideos(events.storyboard)); + } + + encoded.push("//Break Periods"); + + if (events.breaks && events.breaks.length > 0) { + events.breaks.forEach((b) => { + encoded.push( + `${EventType[EventType.Break]},${b.startTime},${b.endTime}` + ); + }); + } + + if (events.storyboard) { + encoded.push(StoryboardEventEncoder.encodeStoryboard(events.storyboard)); + } + + return encoded.join("\n"); + } +} + +class BeatmapGeneralEncoder { + static encodeGeneralSection(beatmap) { + const encoded = ["[General]"]; + const general = beatmap.general; + + encoded.push(`AudioFilename:${general.audioFilename}`); + encoded.push(`AudioLeadIn:${general.audioLeadIn}`); + + if (general.audioHash) { + encoded.push(`AudioHash:${general.audioHash}`); + } + + encoded.push(`PreviewTime:${general.previewTime}`); + encoded.push(`Countdown:${general.countdown}`); + encoded.push(`SampleSet:${SampleSet[general.sampleSet]}`); + encoded.push(`StackLeniency:${general.stackLeniency}`); + encoded.push(`Mode:${beatmap.mode}`); + encoded.push(`LetterboxInBreaks:${+general.letterboxInBreaks}`); + + if (general.storyFireInFront) { + encoded.push(`StoryFireInFront:${+general.storyFireInFront}`); + } + + encoded.push(`UseSkinSprites:${+general.useSkinSprites}`); + + if (general.alwaysShowPlayfield) { + encoded.push(`AlwaysShowPlayfield:${+general.alwaysShowPlayfield}`); + } + + encoded.push(`OverlayPosition:${general.overlayPosition}`); + encoded.push(`SkinPreference:${general.skinPreference}`); + encoded.push(`EpilepsyWarning:${+general.epilepsyWarning}`); + encoded.push(`CountdownOffset:${general.countdownOffset}`); + encoded.push(`SpecialStyle:${+general.specialStyle}`); + encoded.push(`WidescreenStoryboard:${+general.widescreenStoryboard}`); + encoded.push( + `SamplesMatchPlaybackRate:${+general.samplesMatchPlaybackRate}` + ); + + return encoded.join("\n"); + } +} + +class BeatmapHitObjectEncoder { + static encodeHitObjects(beatmap) { + const encoded = ["[HitObjects]"]; + const difficulty = beatmap.difficulty; + const hitObjects = beatmap.hitObjects; + + hitObjects.forEach((hitObject) => { + const general = []; + const position = hitObject.startPosition; + const startPosition = new Vector2( + position ? position.x : 256, + position ? position.y : 192 + ); + + if (beatmap.mode === 3) { + const totalColumns = Math.trunc(Math.max(1, difficulty.circleSize)); + const multiplier = Math.round((512 / totalColumns) * 100000) / 100000; + const column = hitObject.startX; + + startPosition.x = + Math.ceil(column * multiplier) + Math.trunc(multiplier / 2); + } + + general.push(startPosition.toString()); + general.push(hitObject.startTime.toString()); + general.push(hitObject.hitType.toString()); + general.push(hitObject.hitSound.toString()); + + const extras = []; + + if (hitObject.hitType & HitType.Slider) { + const slider = hitObject; + + extras.push(this.encodePathData(slider, startPosition)); + } else if (hitObject.hitType & HitType.Spinner) { + const spinner = hitObject; + + extras.push(this.encodeEndTimeData(spinner)); + } else if (hitObject.hitType & HitType.Hold) { + const hold = hitObject; + + extras.push(this.encodeEndTimeData(hold)); + } + + const set = []; + const normal = hitObject.samples.find( + (s) => s.hitSound === HitSound[HitSound.Normal] + ); + const addition = hitObject.samples.find( + (s) => s.hitSound !== HitSound[HitSound.Normal] + ); + let normalSet = SampleSet.None; + let additionSet = SampleSet.None; + + if (normal) { + normalSet = SampleSet[normal.sampleSet]; + } + + if (addition) { + additionSet = SampleSet[addition.sampleSet]; + } + + set[0] = normalSet.toString(); + set[1] = additionSet.toString(); + set[2] = hitObject.samples[0].customIndex.toString(); + set[3] = hitObject.samples[0].volume.toString(); + set[4] = hitObject.samples[0].filename; + extras.push(set.join(":")); + + const generalLine = general.join(","); + const extrasLine = + hitObject.hitType & HitType.Hold ? extras.join(":") : extras.join(","); + + encoded.push([generalLine, extrasLine].join(",")); + }); + + return encoded.join("\n"); + } + static encodePathData(slider, offset) { + const path = []; + let lastType; + + slider.path.controlPoints.forEach((point, i) => { + if (point.type !== null) { + let needsExplicitSegment = + point.type !== lastType || point.type === PathType.PerfectCurve; + + if (i > 1) { + const p1 = offset.add(slider.path.controlPoints[i - 1].position); + const p2 = offset.add(slider.path.controlPoints[i - 2].position); + + if (~~p1.x === ~~p2.x && ~~p1.y === ~~p2.y) { + needsExplicitSegment = true; + } + } + + if (needsExplicitSegment) { + path.push(slider.path.curveType); + lastType = point.type; + } else { + path.push( + `${offset.x + point.position.x}:${offset.y + point.position.y}` + ); + } + } + + if (i !== 0) { + path.push( + `${offset.x + point.position.x}:${offset.y + point.position.y}` + ); + } + }); + + const data = []; + + data.push(path.join("|")); + data.push((slider.repeats + 1).toString()); + data.push(slider.distance.toString()); + + const adds = []; + const sets = []; + + slider.nodeSamples.forEach((node, nodeIndex) => { + adds[nodeIndex] = HitSound.None; + sets[nodeIndex] = [SampleSet.None, SampleSet.None]; + node.forEach((sample, sampleIndex) => { + if (sampleIndex === 0) { + sets[nodeIndex][0] = SampleSet[sample.sampleSet]; + } else { + adds[nodeIndex] |= HitSound[sample.hitSound]; + sets[nodeIndex][1] = SampleSet[sample.sampleSet]; + } + }); + }); + + data.push(adds.join("|")); + data.push(sets.map((set) => set.join(":")).join("|")); + + return data.join(","); + } + static encodeEndTimeData(hitObject) { + return hitObject.endTime.toString(); + } +} + +class BeatmapMetadataEncoder { + static encodeMetadataSection(beatmap) { + const encoded = ["[Metadata]"]; + const metadata = beatmap.metadata; + + encoded.push(`Title:${metadata.title}`); + encoded.push(`TitleUnicode:${metadata.titleUnicode}`); + encoded.push(`Artist:${metadata.artist}`); + encoded.push(`ArtistUnicode:${metadata.artistUnicode}`); + encoded.push(`Creator:${metadata.creator}`); + encoded.push(`Version:${metadata.version}`); + encoded.push(`Source:${metadata.source}`); + encoded.push(`Tags:${metadata.tags.join(" ")}`); + encoded.push(`BeatmapID:${metadata.beatmapId}`); + encoded.push(`BeatmapSetID:${metadata.beatmapSetId}`); + + return encoded.join("\n"); + } +} + +class BeatmapTimingPointEncoder { + static encodeControlPoints(beatmap) { + const encoded = ["[TimingPoints]"]; + + beatmap.controlPoints.groups.forEach((group) => { + const points = group.controlPoints; + const timing = points.find((c) => c.beatLength); + + if (timing) { + encoded.push(this.encodeGroup(group, true)); + } + + encoded.push(this.encodeGroup(group)); + }); + + return encoded.join("\n"); + } + static encodeGroup(group, useTiming = false) { + const { difficultyPoint, effectPoint, samplePoint, timingPoint } = + this.updateActualPoints(group); + const startTime = group.startTime; + let beatLength = -100; + + if (difficultyPoint !== null) { + beatLength /= difficultyPoint.sliderVelocity; + } + + let sampleSet = SampleSet.None; + let customIndex = 0; + let volume = 100; + + if (samplePoint !== null) { + sampleSet = SampleSet[samplePoint.sampleSet]; + customIndex = samplePoint.customIndex; + volume = samplePoint.volume; + } + + let effects = EffectType.None; + + if (effectPoint !== null) { + const kiai = effectPoint.kiai ? EffectType.Kiai : EffectType.None; + const omitFirstBarLine = effectPoint.omitFirstBarLine + ? EffectType.OmitFirstBarLine + : EffectType.None; + + effects |= kiai | omitFirstBarLine; + } + + let timeSignature = TimeSignature.SimpleQuadruple; + let uninherited = 0; + + if (useTiming && timingPoint !== null) { + beatLength = timingPoint.beatLength; + timeSignature = timingPoint.timeSignature; + uninherited = 1; + } + + return [ + startTime, + beatLength, + timeSignature, + sampleSet, + customIndex, + volume, + uninherited, + effects, + ].join(","); + } + static updateActualPoints(group) { + let timingPoint = null; + + group.controlPoints.forEach((point) => { + if ( + point.pointType === ControlPointType.DifficultyPoint && + !point.isRedundant(this.lastDifficultyPoint) + ) { + this.lastDifficultyPoint = point; + } + + if ( + point.pointType === ControlPointType.EffectPoint && + !point.isRedundant(this.lastEffectPoint) + ) { + this.lastEffectPoint = point; + } + + if ( + point.pointType === ControlPointType.SamplePoint && + !point.isRedundant(this.lastSamplePoint) + ) { + this.lastSamplePoint = point; + } + + if (point.pointType === ControlPointType.TimingPoint) { + timingPoint = point; + } + }); + + return { + timingPoint, + difficultyPoint: this.lastDifficultyPoint, + effectPoint: this.lastEffectPoint, + samplePoint: this.lastSamplePoint, + }; + } +} +BeatmapTimingPointEncoder.lastDifficultyPoint = null; +BeatmapTimingPointEncoder.lastEffectPoint = null; +BeatmapTimingPointEncoder.lastSamplePoint = null; + +class ReplayEncoder { + static encodeLifeBar(frames) { + if (!frames.length) { + return ""; + } + + return frames.map((f) => `${f.startTime}|${f.health}`).join(","); + } + static encodeReplayFrames(frames, beatmap) { + const encoded = []; + + if (frames) { + let lastTime = 0; + + frames.forEach((frame) => { + let _a, _b, _c; + const time = Math.round(frame.startTime); + const legacyFrame = this._getLegacyFrame(frame, beatmap); + const encodedData = [ + time - lastTime, + (_a = + legacyFrame === null || legacyFrame === void 0 + ? void 0 + : legacyFrame.mouseX) !== null && _a !== void 0 + ? _a + : 0, + (_b = + legacyFrame === null || legacyFrame === void 0 + ? void 0 + : legacyFrame.mouseY) !== null && _b !== void 0 + ? _b + : 0, + (_c = + legacyFrame === null || legacyFrame === void 0 + ? void 0 + : legacyFrame.buttonState) !== null && _c !== void 0 + ? _c + : ReplayButtonState.None, + ]; + + encoded.push(encodedData.join("|")); + lastTime = time; + }); + } + + encoded.push("-12345|0|0|0"); + + return encoded.join(","); + } + static _getLegacyFrame(frame, beatmap) { + if (frame instanceof LegacyReplayFrame) { + return frame; + } + + const convertibleFrame = frame; + + if (convertibleFrame.toLegacy) { + return convertibleFrame.toLegacy(beatmap); + } + + throw new Error( + "Some of the replay frames can not be converted to the legacy format!" + ); + } +} + +const textDecoder = new TextDecoder(); + +function concatBufferViews(list) { + if (list.length <= 0) { + return new Uint8Array(0); + } + + const bufferLength = list.reduce((len, buf) => len + buf.byteLength, 0); + const arrayBuffer = new Uint8Array(bufferLength); + + list.reduce((offset, view) => { + arrayBuffer.set(new Uint8Array(view.buffer), offset); + + return offset + view.byteLength; + }, 0); + + return arrayBuffer; +} + +function stringifyBuffer(data) { + if (typeof data === "string") { + return data; + } + + return textDecoder.decode(data); +} + +class SerializationWriter { + constructor() { + this._bytesWritten = 0; + this._views = []; + } + get bytesWritten() { + return this._bytesWritten; + } + finish() { + return concatBufferViews(this._views); + } + writeByte(value) { + return this._update(1, new Uint8Array([value])); + } + writeBytes(value) { + this._bytesWritten += value.byteLength; + this._views.push(value); + + return value.byteLength; + } + writeShort(value) { + return this._update(2, new Uint16Array([value])); + } + writeInteger(value) { + return this._update(4, new Int32Array([value])); + } + writeLong(value) { + return this._update(8, new BigInt64Array([value])); + } + writeDate(date) { + const epochTicks = BigInt(62135596800000); + const ticks = BigInt(date.getTime()); + + return this.writeLong((ticks + epochTicks) * BigInt(1e4)); + } + writeString(value) { + if (value.length === 0) { + return this.writeByte(0x00); + } + + let bytesWritten = this.writeByte(0x0b); + + bytesWritten += this.writeULEB128(value.length); + + const view = new Uint8Array(value.length); + + for (let i = 0; i < value.length; ++i) { + view[i] = value.charCodeAt(i); + bytesWritten++; + } + + this._update(bytesWritten, view); + + return bytesWritten; + } + writeULEB128(value) { + let byte = 0; + let bytesWritten = 0; + + do { + byte = value & 0x7f; + value >>= 7; + + if (value !== 0) { + byte |= 0x80; + } + + bytesWritten += this.writeByte(byte); + } while (value !== 0); + + return bytesWritten; + } + _update(bytesWritten, buffer) { + this._bytesWritten += bytesWritten; + this._views.push(buffer); + + return bytesWritten; + } +} + +class BeatmapEncoder { + async encodeToPath(path, beatmap) { + if (!path.endsWith(FileFormat.Beatmap)) { + path += FileFormat.Beatmap; + } + + try { + await browserFSOperation(browserFSOperation(path), { recursive: true }); + await browserFSOperation(path, await this.encodeToString(beatmap)); + } catch (err) { + const reason = err.message || err; + throw new Error(`Failed to encode a beatmap: ${reason}`); + } + } + encodeToString(beatmap) { + let _a; + + if ( + !(beatmap === null || beatmap === void 0 ? void 0 : beatmap.fileFormat) + ) { + return ""; + } + + const fileFormat = + (_a = beatmap.fileFormat) !== null && _a !== void 0 + ? _a + : BeatmapEncoder.FIRST_LAZER_VERSION; + const encoded = [ + `osu file format v${fileFormat}`, + BeatmapGeneralEncoder.encodeGeneralSection(beatmap), + BeatmapEditorEncoder.encodeEditorSection(beatmap), + BeatmapMetadataEncoder.encodeMetadataSection(beatmap), + BeatmapDifficultyEncoder.encodeDifficultySection(beatmap), + BeatmapEventEncoder.encodeEventSection(beatmap), + BeatmapTimingPointEncoder.encodeControlPoints(beatmap), + BeatmapColorEncoder.encodeColors(beatmap), + BeatmapHitObjectEncoder.encodeHitObjects(beatmap), + ]; + + return encoded.join("\n\n") + "\n"; + } +} +BeatmapEncoder.FIRST_LAZER_VERSION = 128; + +class StoryboardEncoder { + async encodeToPath(path, storyboard) { + if (!path.endsWith(FileFormat.Storyboard)) { + path += FileFormat.Storyboard; + } + + try { + await browserFSOperation(browserFSOperation(path), { recursive: true }); + await browserFSOperation(path, await this.encodeToString(storyboard)); + } catch (err) { + const reason = err.message || err; + throw new Error(`Failed to encode a storyboard: ${reason}`); + } + } + encodeToString(storyboard) { + if (!(storyboard instanceof Storyboard)) { + return ""; + } + + const encoded = [ + `osu file format v${storyboard.fileFormat}`, + StoryboardEventEncoder.encodeEventSection(storyboard), + ]; + + return encoded.join("\n\n") + "\n"; + } +} + +class LZMA { + static async decompress(data) { + try { + return await this._lzma.decompress(data); + } catch { + return ""; + } + } + static async compress(data) { + try { + return await this._lzma.compress(data, 1); + } catch { + return new Uint8Array([]); + } + } +} +LZMA._lzma = getLZMAInstance(); + +function getLZMAInstance() { + return { + decompress(data) { + return new Promise((res, rej) => { + decompress(data, (result, err) => { + err + ? rej(err) + : res( + typeof result === "string" + ? result + : new Uint8Array(result).toString() + ); + }); + }); + }, + compress(data) { + return new Promise((res, rej) => { + compress(data, 1, (result, err) => { + err ? rej(err) : res(new Uint8Array(result)); + }); + }); + }, + }; +} + +class ScoreEncoder { + async encodeToPath(path, score, beatmap) { + if (!path.endsWith(FileFormat.Replay)) { + path += FileFormat.Replay; + } + + const data = await this.encodeToBuffer(score, beatmap); + + try { + await browserFSOperation(browserFSOperation(path), { recursive: true }); + await browserFSOperation(path, new Uint8Array(data)); + } catch (err) { + const reason = err.message || err; + throw new Error(`Failed to encode a score: ${reason}`); + } + } + async encodeToBuffer(score, beatmap) { + let _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; + + if ( + typeof ((_a = + score === null || score === void 0 ? void 0 : score.info) === null || + _a === void 0 + ? void 0 + : _a.id) !== "number" + ) { + return new Uint8Array(); + } + + const writer = new SerializationWriter(); + + try { + writer.writeByte(score.info.rulesetId); + writer.writeInteger( + (_c = + (_b = score.replay) === null || _b === void 0 + ? void 0 + : _b.gameVersion) !== null && _c !== void 0 + ? _c + : ScoreEncoder.DEFAULT_GAME_VERSION + ); + writer.writeString( + (_d = score.info.beatmapHashMD5) !== null && _d !== void 0 ? _d : "" + ); + writer.writeString(score.info.username); + writer.writeString( + (_f = + (_e = score.replay) === null || _e === void 0 + ? void 0 + : _e.hashMD5) !== null && _f !== void 0 + ? _f + : "" + ); + writer.writeShort(score.info.count300); + writer.writeShort(score.info.count100); + writer.writeShort(score.info.count50); + writer.writeShort(score.info.countGeki); + writer.writeShort(score.info.countKatu); + writer.writeShort(score.info.countMiss); + writer.writeInteger(score.info.totalScore); + writer.writeShort(score.info.maxCombo); + writer.writeByte(Number(score.info.perfect)); + writer.writeInteger( + ((_h = + (_g = score.info.mods) === null || _g === void 0 + ? void 0 + : _g.bitwise) !== null && _h !== void 0 + ? _h + : Number(score.info.rawMods)) || 0 + ); + writer.writeString( + ReplayEncoder.encodeLifeBar( + (_k = + (_j = score.replay) === null || _j === void 0 + ? void 0 + : _j.lifeBar) !== null && _k !== void 0 + ? _k + : [] + ) + ); + writer.writeDate(score.info.date); + + if (score.replay) { + const replayData = ReplayEncoder.encodeReplayFrames( + score.replay.frames, + beatmap + ); + const encodedData = await LZMA.compress(replayData); + + writer.writeInteger(encodedData.byteLength); + writer.writeBytes(encodedData); + } else { + writer.writeInteger(0); + } + + writer.writeLong(BigInt(score.info.id)); + + return writer.finish(); + } catch (err) { + const reason = err.message || err; + throw new Error(`Failed to encode a score: '${reason}'`); + } + } +} +ScoreEncoder.DEFAULT_GAME_VERSION = 20230621; + +class BeatmapHitObjectDecoder { + static handleLine(line, beatmap, offset) { + const data = line.split(",").map((v) => v.trim()); + const hitType = Parsing.parseInt(data[3]); + const hitObject = this.createHitObject(hitType); + + hitObject.startX = Parsing.parseInt(data[0], Parsing.MAX_COORDINATE_VALUE); + hitObject.startY = Parsing.parseInt(data[1], Parsing.MAX_COORDINATE_VALUE); + hitObject.startTime = Parsing.parseFloat(data[2]) + offset; + hitObject.hitType = hitType; + hitObject.hitSound = Parsing.parseInt(data[4]); + + const bankInfo = new SampleBank(); + + this.addExtras( + data.slice(5), + hitObject, + bankInfo, + offset, + beatmap.fileFormat + ); + this.addComboOffset(hitObject, beatmap); + + if (hitObject.samples.length === 0) { + hitObject.samples = this.convertSoundType(hitObject.hitSound, bankInfo); + } + + beatmap.hitObjects.push(hitObject); + } + static addComboOffset(hitObject, beatmap) { + const isStandard = beatmap.originalMode === 0; + const isCatch = beatmap.originalMode === 2; + + if (!isStandard && !isCatch) { + return; + } + + const comboObject = hitObject; + const comboOffset = Math.trunc( + (hitObject.hitType & HitType.ComboOffset) >> 4 + ); + const newCombo = !!(hitObject.hitType & HitType.NewCombo); + + if ( + hitObject.hitType & HitType.Normal || + hitObject.hitType & HitType.Slider + ) { + comboObject.isNewCombo = newCombo || this._forceNewCombo; + comboObject.comboOffset = comboOffset + this._extraComboOffset; + this._forceNewCombo = false; + this._extraComboOffset = 0; + } + + if (hitObject.hitType & HitType.Spinner) { + this._forceNewCombo = beatmap.fileFormat <= 8 || newCombo || false; + this._extraComboOffset += comboOffset; + } + } + static addExtras(data, hitObject, bankInfo, offset, fileFormat) { + if (hitObject.hitType & HitType.Normal && data.length > 0) { + this.readCustomSampleBanks(data[0], bankInfo); + } + + if (hitObject.hitType & HitType.Slider) { + return this.addSliderExtras(data, hitObject, bankInfo, fileFormat); + } + + if (hitObject.hitType & HitType.Spinner) { + return this.addSpinnerExtras(data, hitObject, bankInfo, offset); + } + + if (hitObject.hitType & HitType.Hold) { + return this.addHoldExtras(data, hitObject, bankInfo, offset); + } + } + static addSliderExtras(extras, slider, bankInfo, fileFormat) { + const pathString = extras[0]; + const offset = slider.startPosition; + const repeats = Parsing.parseInt(extras[1]); + + if (slider.repeats > 9000) { + throw new Error("Repeat count is way too high"); + } + + slider.repeats = Math.max(0, repeats - 1); + slider.path.controlPoints = this.convertPathString( + pathString, + offset, + fileFormat + ); + slider.path.curveType = slider.path.controlPoints[0].type; + + if (extras.length > 2) { + const length = Parsing.parseFloat( + extras[2], + Parsing.MAX_COORDINATE_VALUE + ); + + slider.path.expectedDistance = Math.max(0, length); + } + + if (extras.length > 5) { + this.readCustomSampleBanks(extras[5], bankInfo); + } + + slider.samples = this.convertSoundType(slider.hitSound, bankInfo); + slider.nodeSamples = this.getSliderNodeSamples(extras, slider, bankInfo); + } + static addSpinnerExtras(extras, spinner, bankInfo, offset) { + spinner.endTime = Parsing.parseInt(extras[0]) + offset; + + if (extras.length > 1) { + this.readCustomSampleBanks(extras[1], bankInfo); + } + } + static addHoldExtras(extras, hold, bankInfo, offset) { + hold.endTime = hold.startTime; + + if (extras.length > 0 && extras[0]) { + const ss = extras[0].split(":"); + + hold.endTime = Math.max(hold.endTime, Parsing.parseFloat(ss[0])) + offset; + this.readCustomSampleBanks(ss.slice(1).join(":"), bankInfo); + } + } + static getSliderNodeSamples(extras, slider, bankInfo) { + const nodes = slider.repeats + 2; + const nodeBankInfos = []; + + for (let i = 0; i < nodes; ++i) { + nodeBankInfos.push(bankInfo.clone()); + } + + if (extras.length > 4 && extras[4].length > 0) { + const sets = extras[4].split("|"); + + for (let i = 0; i < nodes; ++i) { + if (i >= sets.length) { + break; + } + + this.readCustomSampleBanks(sets[i], nodeBankInfos[i]); + } + } + + const nodeSoundTypes = []; + + for (let i = 0; i < nodes; ++i) { + nodeSoundTypes.push(slider.hitSound); + } + + if (extras.length > 3 && extras[3].length > 0) { + const adds = extras[3].split("|"); + + for (let i = 0; i < nodes; ++i) { + if (i >= adds.length) { + break; + } + + nodeSoundTypes[i] = parseInt(adds[i]) || HitSound.None; + } + } + + const nodeSamples = []; + + for (let i = 0; i < nodes; i++) { + nodeSamples.push( + this.convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]) + ); + } + + return nodeSamples; + } + static convertPathString(pathString, offset, fileFormat) { + const pathSplit = pathString.split("|").map((p) => p.trim()); + const controlPoints = []; + let startIndex = 0; + let endIndex = 0; + let isFirst = true; + + while (++endIndex < pathSplit.length) { + if (pathSplit[endIndex].length > 1) { + continue; + } + + const points = pathSplit.slice(startIndex, endIndex); + const endPoint = + endIndex < pathSplit.length - 1 ? pathSplit[endIndex + 1] : null; + const convertedPoints = this.convertPoints( + points, + endPoint, + isFirst, + offset, + fileFormat + ); + + for (const point of convertedPoints) { + controlPoints.push(...point); + } + + startIndex = endIndex; + isFirst = false; + } + + if (endIndex > startIndex) { + const points = pathSplit.slice(startIndex, endIndex); + const convertedPoints = this.convertPoints( + points, + null, + isFirst, + offset, + fileFormat + ); + + for (const point of convertedPoints) { + controlPoints.push(...point); + } + } + + return controlPoints; + } + static *convertPoints(points, endPoint, isFirst, offset, fileFormat) { + const readOffset = isFirst ? 1 : 0; + const endPointLength = endPoint !== null ? 1 : 0; + const vertices = []; + + if (readOffset === 1) { + vertices[0] = new PathPoint(); + } + + for (let i = 1; i < points.length; ++i) { + vertices[readOffset + i - 1] = readPoint(points[i], offset); + } + + if (endPoint !== null) { + vertices[vertices.length - 1] = readPoint(endPoint, offset); + } + + let type = this.convertPathType(points[0]); + + if (type === PathType.PerfectCurve) { + if (vertices.length !== 3) { + type = PathType.Bezier; + } else if (isLinear(vertices)) { + type = PathType.Linear; + } + } + + vertices[0].type = type; + + let startIndex = 0; + let endIndex = 0; + + while (++endIndex < vertices.length - endPointLength) { + if ( + !vertices[endIndex].position.equals(vertices[endIndex - 1].position) + ) { + continue; + } + + const isStableBeatmap = fileFormat < BeatmapEncoder.FIRST_LAZER_VERSION; + + if (type === PathType.Catmull && endIndex > 1 && isStableBeatmap) { + continue; + } + + if (endIndex === vertices.length - endPointLength - 1) { + continue; + } + + vertices[endIndex - 1].type = type; + yield vertices.slice(startIndex, endIndex); + startIndex = endIndex + 1; + } + + if (endIndex > startIndex) { + yield vertices.slice(startIndex, endIndex); + } + + function readPoint(point, offset) { + const coords = point.split(":").map((v) => { + return Math.trunc(Parsing.parseFloat(v, Parsing.MAX_COORDINATE_VALUE)); + }); + const pos = new Vector2(coords[0], coords[1]).fsubtract(offset); + + return new PathPoint(pos); + } + + function isLinear(p) { + const yx = + (p[1].position.y - p[0].position.y) * + (p[2].position.x - p[0].position.x); + const xy = + (p[1].position.x - p[0].position.x) * + (p[2].position.y - p[0].position.y); + const acceptableDifference = 0.001; + + return Math.abs(yx - xy) < acceptableDifference; + } + } + static convertPathType(type) { + switch (type) { + default: + case "C": + return PathType.Catmull; + case "B": + return PathType.Bezier; + case "L": + return PathType.Linear; + case "P": + return PathType.PerfectCurve; + } + } + static readCustomSampleBanks(hitSample, bankInfo) { + if (!hitSample) { + return; + } + + const split = hitSample.split(":"); + + bankInfo.normalSet = Parsing.parseInt(split[0]); + bankInfo.additionSet = Parsing.parseInt(split[1]); + + if (bankInfo.additionSet === SampleSet.None) { + bankInfo.additionSet = bankInfo.normalSet; + } + + if (split.length > 2) { + bankInfo.customIndex = Parsing.parseInt(split[2]); + } + + if (split.length > 3) { + bankInfo.volume = Math.max(0, Parsing.parseInt(split[3])); + } + + bankInfo.filename = split.length > 4 ? split[4] : ""; + } + static convertSoundType(type, bankInfo) { + if (bankInfo.filename) { + const sample = new HitSample(); + + sample.filename = bankInfo.filename; + sample.volume = bankInfo.volume; + + return [sample]; + } + + const soundTypes = [new HitSample()]; + + soundTypes[0].hitSound = HitSound[HitSound.Normal]; + soundTypes[0].sampleSet = SampleSet[bankInfo.normalSet]; + soundTypes[0].isLayered = + type !== HitSound.None && !(type & HitSound.Normal); + + if (type & HitSound.Finish) { + const sample = new HitSample(); + + sample.hitSound = HitSound[HitSound.Finish]; + soundTypes.push(sample); + } + + if (type & HitSound.Whistle) { + const sample = new HitSample(); + + sample.hitSound = HitSound[HitSound.Whistle]; + soundTypes.push(sample); + } + + if (type & HitSound.Clap) { + const sample = new HitSample(); + + sample.hitSound = HitSound[HitSound.Clap]; + soundTypes.push(sample); + } + + soundTypes.forEach((sound, i) => { + sound.sampleSet = + i !== 0 + ? SampleSet[bankInfo.additionSet] + : SampleSet[bankInfo.normalSet]; + + sound.volume = bankInfo.volume; + sound.customIndex = 0; + + if (bankInfo.customIndex >= 2) { + sound.customIndex = bankInfo.customIndex; + } + }); + + return soundTypes; + } + static createHitObject(hitType) { + if (hitType & HitType.Normal) { + return new HittableObject(); + } + + if (hitType & HitType.Slider) { + return new SlidableObject(); + } + + if (hitType & HitType.Spinner) { + return new SpinnableObject(); + } + + if (hitType & HitType.Hold) { + return new HoldableObject(); + } + + throw new Error(`Unknown hit object type: ${hitType}!`); + } +} +BeatmapHitObjectDecoder._forceNewCombo = false; +BeatmapHitObjectDecoder._extraComboOffset = 0; + +class BeatmapMetadataDecoder { + static handleLine(line, beatmap) { + const [key, ...values] = line.split(":"); + const value = values.join(":").trim(); + + switch (key.trim()) { + case "Title": + beatmap.metadata.title = value; + break; + case "TitleUnicode": + beatmap.metadata.titleUnicode = value; + break; + case "Artist": + beatmap.metadata.artist = value; + break; + case "ArtistUnicode": + beatmap.metadata.artistUnicode = value; + break; + case "Creator": + beatmap.metadata.creator = value; + break; + case "Version": + beatmap.metadata.version = value; + break; + case "Source": + beatmap.metadata.source = value; + break; + case "Tags": + beatmap.metadata.tags = value.split(" "); + break; + case "BeatmapID": + beatmap.metadata.beatmapId = Parsing.parseInt(value); + break; + case "BeatmapSetID": + beatmap.metadata.beatmapSetId = Parsing.parseInt(value); + } + } +} + +class BeatmapTimingPointDecoder { + static handleLine(line, beatmap, offset) { + this.controlPoints = beatmap.controlPoints; + + const data = line.split(","); + let timeSignature = TimeSignature.SimpleQuadruple; + let sampleSet = SampleSet[SampleSet.None]; + let customIndex = 0; + let volume = 100; + let timingChange = true; + let effects = EffectType.None; + + if (data.length > 2) { + switch (data.length) { + default: + case 8: + effects = Parsing.parseInt(data[7]); + case 7: + timingChange = data[6] === "1"; + case 6: + volume = Parsing.parseInt(data[5]); + case 5: + customIndex = Parsing.parseInt(data[4]); + case 4: + sampleSet = SampleSet[Parsing.parseInt(data[3])]; + case 3: + timeSignature = Parsing.parseInt(data[2]); + } + } + + if (timeSignature < 1) { + throw new Error("The numerator of a time signature must be positive."); + } + + const startTime = Parsing.parseFloat(data[0]) + offset; + const beatLength = Parsing.parseFloat( + data[1], + Parsing.MAX_PARSE_VALUE, + true + ); + let bpmMultiplier = 1; + let speedMultiplier = 1; + + if (beatLength < 0) { + speedMultiplier = 100 / -beatLength; + bpmMultiplier = Math.min(Math.fround(-beatLength), 10000); + bpmMultiplier = Math.max(10, bpmMultiplier) / 100; + } + + if (timingChange && Number.isNaN(beatLength)) { + throw new Error("Beat length cannot be NaN in a timing control point"); + } + + if (timingChange) { + const timingPoint = new TimingPoint(); + + timingPoint.beatLength = beatLength; + timingPoint.timeSignature = timeSignature; + this.addControlPoint(timingPoint, startTime, true); + } + + const difficultyPoint = new DifficultyPoint(); + + difficultyPoint.bpmMultiplier = bpmMultiplier; + difficultyPoint.sliderVelocity = speedMultiplier; + difficultyPoint.generateTicks = !Number.isNaN(beatLength); + difficultyPoint.isLegacy = true; + this.addControlPoint(difficultyPoint, startTime, timingChange); + + const effectPoint = new EffectPoint(); + + effectPoint.kiai = (effects & EffectType.Kiai) > 0; + effectPoint.omitFirstBarLine = (effects & EffectType.OmitFirstBarLine) > 0; + + if (beatmap.originalMode === 1 || beatmap.originalMode === 3) { + effectPoint.scrollSpeed = speedMultiplier; + } + + this.addControlPoint(effectPoint, startTime, timingChange); + + const samplePoint = new SamplePoint(); + + samplePoint.sampleSet = sampleSet; + samplePoint.customIndex = customIndex; + samplePoint.volume = volume; + this.addControlPoint(samplePoint, startTime, timingChange); + } + static addControlPoint(point, time, timingChange) { + if (time !== this.pendingTime) { + this.flushPendingPoints(); + } + + timingChange + ? this.pendingPoints.unshift(point) + : this.pendingPoints.push(point); + + this.pendingTime = time; + } + static flushPendingPoints() { + const pendingTime = this.pendingTime; + const pendingPoints = this.pendingPoints; + const controlPoints = this.controlPoints; + const pendingTypes = this.pendingTypes; + let i = pendingPoints.length; + + while (--i >= 0) { + if (pendingTypes.includes(pendingPoints[i].pointType)) { + continue; + } + + pendingTypes.push(pendingPoints[i].pointType); + controlPoints.add(pendingPoints[i], pendingTime); + } + + this.pendingPoints = []; + this.pendingTypes = []; + } +} +BeatmapTimingPointDecoder.pendingTime = 0; +BeatmapTimingPointDecoder.pendingTypes = []; +BeatmapTimingPointDecoder.pendingPoints = []; + +class ReplayDecoder { + static decodeLifeBar(data) { + if (!data) { + return []; + } + + const lifeBarFrames = []; + const frames = data.split(","); + + for (let i = 0; i < frames.length; ++i) { + if (!frames[i]) { + continue; + } + + const frameData = frames[i].split("|"); + + if (frameData.length < 2) { + continue; + } + + const frame = this.handleLifeBarFrame(frameData); + + lifeBarFrames.push(frame); + } + + return lifeBarFrames; + } + static handleLifeBarFrame(frameData) { + return new LifeBarFrame( + Parsing.parseInt(frameData[0]), + Parsing.parseFloat(frameData[1]) + ); + } + static decodeReplayFrames(data) { + if (!data) { + return []; + } + + let lastTime = 0; + const replayFrames = []; + const frames = data.split(","); + + for (let i = 0; i < frames.length; ++i) { + if (!frames[i]) { + continue; + } + + const frameData = frames[i].split("|"); + + if (frameData.length < 4) { + continue; + } + + if (frameData[0] === "-12345") { + continue; + } + + const replayFrame = this.handleReplayFrame(frameData); + + lastTime += replayFrame.interval; + + if (i < 2 && replayFrame.mouseX === 256 && replayFrame.mouseY === -500) { + continue; + } + + if (replayFrame.interval < 0) { + continue; + } + + replayFrame.startTime = lastTime; + replayFrames.push(replayFrame); + } + + return replayFrames; + } + static handleReplayFrame(frameData) { + return new LegacyReplayFrame( + 0.0, + Parsing.parseFloat(frameData[0]), + new Vector2( + Parsing.parseFloat(frameData[1], Parsing.MAX_COORDINATE_VALUE), + Parsing.parseFloat(frameData[2], Parsing.MAX_COORDINATE_VALUE) + ), + Parsing.parseInt(frameData[3]) + ); + } +} + +class SerializationReader { + constructor(buffer) { + this._bytesRead = 0; + this.view = new DataView(new Uint8Array(buffer).buffer); + } + get bytesRead() { + return this._bytesRead; + } + get remainingBytes() { + return this.view.byteLength - this._bytesRead; + } + readByte() { + return this.view.getUint8(this._bytesRead++); + } + readBytes(length) { + const bytes = this.view.buffer.slice( + this._bytesRead, + this._bytesRead + length + ); + + this._bytesRead += length; + + return new Uint8Array(bytes); + } + readShort() { + const value = this.view.getUint16(this._bytesRead, true); + + this._bytesRead += 2; + + return value; + } + readInteger() { + const value = this.view.getInt32(this._bytesRead, true); + + this._bytesRead += 4; + + return value; + } + readLong() { + const value = this.view.getBigInt64(this._bytesRead, true); + + this._bytesRead += 8; + + return value; + } + readDate() { + const epochTicks = 62135596800000; + + return new Date(Number(this.readLong() / BigInt(1e4)) - epochTicks); + } + readULEB128() { + let val = 0; + let shift = 0; + let byte = 0; + + do { + byte = this.readByte(); + val |= (byte & 0x7f) << shift; + shift += 7; + } while ((byte & 0x80) !== 0); + + return val; + } + readString() { + if (this.readByte() !== 0x0b) { + return ""; + } + + const length = this.readULEB128(); + + return length > 0 ? stringifyBuffer(this.readBytes(length)) : ""; + } +} + +class StoryboardGeneralDecoder { + static handleLine(line, storyboard) { + const [key, ...values] = line.split(":").map((v) => v.trim()); + const value = values.join(" "); + + switch (key) { + case "UseSkinSprites": + storyboard.useSkinSprites = value === "1"; + } + } +} + +class StoryboardVariableDecoder { + static handleLine(line, variables) { + if (!line.startsWith("$")) { + return; + } + + const pair = line.split("="); + + if (pair.length === 2) { + variables.set(pair[0], pair[1].trimEnd()); + } + } + static decodeVariables(line, variables) { + if (!line.includes("$") || !variables.size) { + return line; + } + + variables.forEach((value, key) => { + line = line.replace(key, value); + }); + + return line; + } +} + +class Decoder { + async _getFileBuffer(path) { + try { + await browserFSOperation(path); + } catch { + throw new Error("File doesn't exist!"); + } + + try { + return await browserFSOperation(path); + } catch { + throw new Error("File can't be read!"); + } + } + async _getFileUpdateDate(path) { + try { + return (await browserFSOperation(path)).mtime; + } catch { + throw new Error("Failed to get last file update date!"); + } + } +} + +class SectionMap extends Map { + constructor() { + super(...arguments); + this.currentSection = null; + } + get(section) { + let _a; + + return (_a = super.get(section)) !== null && _a !== void 0 ? _a : false; + } + set(section, state = true) { + return super.set(section, state); + } + reset() { + this.forEach((_, key, map) => { + map.set(key, true); + }); + + this.currentSection = null; + + return this; + } + get hasEnabledSections() { + for (const state of this.values()) { + if (state) { + return true; + } + } + + return false; + } + get isSectionEnabled() { + return this.currentSection ? this.get(this.currentSection) : false; + } +} + +class SectionDecoder extends Decoder { + constructor() { + super(...arguments); + this._lines = null; + this._sectionMap = new SectionMap(); + } + _getLines(data) { + let lines = null; + + if (data.constructor === Array) { + lines = data; + } + + if (!lines || !lines.length) { + throw new Error("Data not found!"); + } + + return lines; + } + _parseLine(line, output) { + if (this._shouldSkipLine(line)) { + return LineType.Empty; + } + + line = this._preprocessLine(line); + + if (line.includes("osu file format v")) { + return LineType.FileFormat; + } + + if (line.startsWith("[") && line.endsWith("]")) { + const section = line.slice(1, -1); + + if (this._sectionMap.currentSection) { + this._sectionMap.set(this._sectionMap.currentSection, false); + this._sectionMap.currentSection = null; + } + + if (!this._sectionMap.hasEnabledSections) { + return LineType.Break; + } + + if (section in Section) { + this._sectionMap.currentSection = section; + } + + return LineType.Section; + } + + if (!this._sectionMap.isSectionEnabled) { + return LineType.Empty; + } + + try { + this._parseSectionData(line, output); + + return LineType.Data; + } catch { + return LineType.Empty; + } + } + _parseSectionData(line, output) { + const outputWithColors = output; + + if (this._sectionMap.currentSection !== Section.Colours) { + return; + } + + if ( + !(outputWithColors === null || outputWithColors === void 0 + ? void 0 + : outputWithColors.colors) + ) { + return; + } + + BeatmapColorDecoder.handleLine(line, outputWithColors); + } + _preprocessLine(line) { + if (this._sectionMap.currentSection !== Section.Metadata) { + line = this._stripComments(line); + } + + return line.trimEnd(); + } + _shouldSkipLine(line) { + return typeof line !== "string" || !line || line.startsWith("//"); + } + _stripComments(line) { + const index = line.indexOf("//"); + + return index > 0 ? line.substring(0, index) : line; + } + _reset() { + this._sectionMap.reset(); + this._lines = null; + } + _setEnabledSections(options) { + this._sectionMap.set( + Section.Colours, + options === null || options === void 0 ? void 0 : options.parseColours + ); + } +} + +class StoryboardDecoder extends SectionDecoder { + constructor() { + super(...arguments); + this._variables = new Map(); + } + async decodeFromPath(firstPath, secondPath) { + if ( + !firstPath.endsWith(FileFormat.Beatmap) && + !firstPath.endsWith(FileFormat.Storyboard) + ) { + throw new Error( + `Wrong format of the first file! Only ${FileFormat.Beatmap} and ${FileFormat.Storyboard} files are supported!` + ); + } + + if (typeof secondPath === "string") { + if (!secondPath.endsWith(FileFormat.Storyboard)) { + throw new Error( + `Wrong format of the second file! Only ${FileFormat.Storyboard} files are supported as a second argument!` + ); + } + } + + try { + const firstData = await this._getFileBuffer(firstPath); + const secondData = + typeof secondPath === "string" + ? await this._getFileBuffer(firstPath) + : undefined; + + return this.decodeFromBuffer(firstData, secondData); + } catch (err) { + const reason = err.message || err; + throw new Error(`Failed to decode a storyboard: '${reason}'`); + } + } + decodeFromBuffer(firstBuffer, secondBuffer) { + const firstString = stringifyBuffer(firstBuffer); + const secondString = secondBuffer + ? stringifyBuffer(secondBuffer) + : undefined; + + return this.decodeFromString(firstString, secondString); + } + decodeFromString(firstString, secondString) { + if (typeof firstString !== "string") { + firstString = String(firstString); + } + + if ( + typeof secondString !== "string" && + typeof secondString !== "undefined" + ) { + secondString = String(secondString); + } + + const firstData = firstString.split(/\r?\n/); + const secondData = + secondString === null || secondString === void 0 + ? void 0 + : secondString.split(/\r?\n/); + + return this.decodeFromLines(firstData, secondData); + } + decodeFromLines(firstData, secondData) { + const storyboard = new Storyboard(); + + this._reset(); + this._setEnabledSections(); + this._lines = [ + ...this._getLines(firstData), + ...(secondData ? this._getLines(secondData) : []), + ]; + + for (let i = 0; i < this._lines.length; ++i) { + const type = this._parseLine(this._lines[i], storyboard); + + if (type === LineType.Break) { + break; + } + } + + storyboard.variables = this._variables; + + return storyboard; + } + _parseLine(line, storyboard) { + if (line.includes("osu file format v")) { + storyboard.fileFormat = Parsing.parseInt(line.split("v")[1]); + + return LineType.FileFormat; + } + + return super._parseLine(line, storyboard); + } + _parseSectionData(line, storyboard) { + switch (this._sectionMap.currentSection) { + case Section.General: + return StoryboardGeneralDecoder.handleLine(line, storyboard); + case Section.Events: + return StoryboardEventDecoder.handleLine(line, storyboard); + case Section.Variables: + return StoryboardVariableDecoder.handleLine(line, this._variables); + } + + super._parseSectionData(line, storyboard); + } + _setEnabledSections() { + super._setEnabledSections(); + this._sectionMap.set(Section.General); + this._sectionMap.set(Section.Variables); + this._sectionMap.set(Section.Events); + } + _preprocessLine(line) { + line = StoryboardVariableDecoder.decodeVariables(line, this._variables); + + return super._preprocessLine(line); + } + _reset() { + super._reset(); + this._sectionMap.reset(); + this._sectionMap.currentSection = Section.Events; + } +} + +class BeatmapDecoder extends SectionDecoder { + constructor() { + super(...arguments); + this._offset = 0; + this._sbLines = null; + } + async decodeFromPath(path, options) { + if (!path.endsWith(FileFormat.Beatmap)) { + throw new Error( + `Wrong file format! Only ${FileFormat.Beatmap} files are supported!` + ); + } + + try { + const data = await this._getFileBuffer(path); + const beatmap = this.decodeFromBuffer(data, options); + + beatmap.fileUpdateDate = await this._getFileUpdateDate(path); + + return beatmap; + } catch (err) { + const reason = err.message || err; + throw new Error(`Failed to decode a beatmap: ${reason}`); + } + } + decodeFromBuffer(data, options) { + return this.decodeFromString(stringifyBuffer(data), options); + } + decodeFromString(str, options) { + str = typeof str !== "string" ? String(str) : str; + + return this.decodeFromLines(str.split(/\r?\n/), options); + } + decodeFromLines(data, options) { + const beatmap = new Beatmap(); + + this._reset(); + this._lines = this._getLines(data); + this._setEnabledSections(typeof options !== "boolean" ? options : {}); + this._sbLines = this._shouldParseStoryboard(options) ? [] : null; + + const fileFormatLine = this._lines[0].trim(); + + if (!fileFormatLine.startsWith("osu file format v")) { + throw new Error("Not a valid beatmap!"); + } + + for (let i = 0; i < this._lines.length; ++i) { + const type = this._parseLine(this._lines[i], beatmap); + + if (type === LineType.Break) { + break; + } + } + + BeatmapTimingPointDecoder.flushPendingPoints(); + + for (let i = 0; i < beatmap.hitObjects.length; ++i) { + beatmap.hitObjects[i].applyDefaults( + beatmap.controlPoints, + beatmap.difficulty + ); + } + + beatmap.hitObjects.sort((a, b) => a.startTime - b.startTime); + + if (this._sbLines && this._sbLines.length) { + const storyboardDecoder = new StoryboardDecoder(); + + beatmap.events.storyboard = storyboardDecoder.decodeFromLines( + this._sbLines + ); + beatmap.events.storyboard.useSkinSprites = beatmap.general.useSkinSprites; + beatmap.events.storyboard.colors = beatmap.colors; + beatmap.events.storyboard.fileFormat = beatmap.fileFormat; + } + + return beatmap; + } + _parseLine(line, beatmap) { + if (line.includes("osu file format v")) { + beatmap.fileFormat = Parsing.parseInt(line.split("v")[1]); + + return LineType.FileFormat; + } + + return super._parseLine(line, beatmap); + } + _parseSectionData(line, beatmap) { + switch (this._sectionMap.currentSection) { + case Section.General: + return BeatmapGeneralDecoder.handleLine(line, beatmap, this._offset); + case Section.Editor: + return BeatmapEditorDecoder.handleLine(line, beatmap); + case Section.Metadata: + return BeatmapMetadataDecoder.handleLine(line, beatmap); + case Section.Difficulty: + return BeatmapDifficultyDecoder.handleLine(line, beatmap); + case Section.Events: + return BeatmapEventDecoder.handleLine( + line, + beatmap, + this._sbLines, + this._offset + ); + case Section.TimingPoints: + return BeatmapTimingPointDecoder.handleLine( + line, + beatmap, + this._offset + ); + case Section.HitObjects: + return BeatmapHitObjectDecoder.handleLine(line, beatmap, this._offset); + } + + super._parseSectionData(line, beatmap); + } + _setEnabledSections(options) { + super._setEnabledSections(options); + this._sectionMap.set( + Section.General, + options === null || options === void 0 ? void 0 : options.parseGeneral + ); + this._sectionMap.set( + Section.Editor, + options === null || options === void 0 ? void 0 : options.parseEditor + ); + this._sectionMap.set( + Section.Metadata, + options === null || options === void 0 ? void 0 : options.parseMetadata + ); + this._sectionMap.set( + Section.Difficulty, + options === null || options === void 0 ? void 0 : options.parseDifficulty + ); + this._sectionMap.set( + Section.Events, + options === null || options === void 0 ? void 0 : options.parseEvents + ); + this._sectionMap.set( + Section.TimingPoints, + options === null || options === void 0 + ? void 0 + : options.parseTimingPoints + ); + this._sectionMap.set( + Section.HitObjects, + options === null || options === void 0 ? void 0 : options.parseHitObjects + ); + } + _shouldParseStoryboard(options) { + let _a, _b; + const parsingOptions = options; + const storyboardFlag = + (_a = + parsingOptions === null || parsingOptions === void 0 + ? void 0 + : parsingOptions.parseStoryboard) !== null && _a !== void 0 + ? _a + : options; + const parseSb = typeof storyboardFlag === "boolean" ? storyboardFlag : true; + const parseEvents = + (_b = + parsingOptions === null || parsingOptions === void 0 + ? void 0 + : parsingOptions.parseEvents) !== null && _b !== void 0 + ? _b + : true; + + return parseEvents && parseSb; + } +} +BeatmapDecoder.EARLY_VERSION_TIMING_OFFSET = 24; + +class ScoreDecoder extends Decoder { + async decodeFromPath(path, parseReplay = true) { + if (!path.endsWith(FileFormat.Replay)) { + throw new Error( + `Wrong file format! Only ${FileFormat.Replay} files are supported!` + ); + } + + try { + const data = await this._getFileBuffer(path); + + return await this.decodeFromBuffer(data, parseReplay); + } catch (err) { + const reason = err.message || err; + throw new Error(`Failed to decode a score: '${reason}'`); + } + } + async decodeFromBuffer(buffer, parseReplay = true) { + const scoreInfo = new ScoreInfo(); + + scoreInfo.id = 2398933774; + scoreInfo.accuracy = 0; + scoreInfo.count50 = 0; + scoreInfo.count100 = 0; + scoreInfo.count300 = 0; + scoreInfo.countGeki = 0; + scoreInfo.countKatu = 0; + scoreInfo.countMiss = 0; + scoreInfo.totalScore = 0; + scoreInfo.maxCombo = 0; + scoreInfo.perfect = false; + scoreInfo.rawMods = 0; + scoreInfo.date = new Date(); + scoreInfo.beatmapHashMD5 = "d41d8cd98f00b204e9800998ecf8427e"; + scoreInfo.username = "Unknown"; + scoreInfo.rulesetId = 0; + + let replay = null; + + replay = new Replay(); + + replay.rawFrames = buffer; + replay.frames = ReplayDecoder.decodeReplayFrames(buffer); + + + return new Score(scoreInfo, replay); + } + _parseScoreId(version, reader) { + if (version >= 20140721) { + return Number(reader.readLong()); + } + + if (version >= 20121008) { + return reader.readInteger(); + } + + return 0; + } +} + +export { + BeatmapDecoder, + BeatmapEncoder, + HittableObject, + HoldableObject, + ScoreDecoder, + ScoreEncoder, + SlidableObject, + SpinnableObject, + StoryboardDecoder, + StoryboardEncoder, +}; diff --git a/nise-replay-viewer/osu-parsers/lib/index.d.ts b/nise-replay-viewer/osu-parsers/lib/index.d.ts new file mode 100644 index 0000000..5b7c004 --- /dev/null +++ b/nise-replay-viewer/osu-parsers/lib/index.d.ts @@ -0,0 +1,528 @@ +import { Beatmap, Storyboard, Score, IBeatmap, IScore, HitObject, IHasCombo, IHoldableObject, HitSample, ISlidableObject, SliderPath, ControlPointInfo, BeatmapDifficultySection, ISpinnableObject } from 'osu-classes'; + +declare enum LineType { + FileFormat = 0, + Section = 1, + Empty = 2, + Data = 3, + Break = 4 +} + +declare enum Section { + General = 'General', + Editor = 'Editor', + Metadata = 'Metadata', + Difficulty = 'Difficulty', + Events = 'Events', + TimingPoints = 'TimingPoints', + Colours = 'Colours', + HitObjects = 'HitObjects', + Variables = 'Variables', + Fonts = 'Fonts', + CatchTheBeat = 'CatchTheBeat', + Mania = 'Mania' +} + +interface IParsingOptions { + /** + * Whether to parse colour section or not. + * This section is only required for beatmap & storyboard rendering stuff + * and doesn't affect beatmap parsing and difficulty & performance calculation at all. + */ + parseColours?: boolean; +} +interface IBeatmapParsingOptions extends IParsingOptions { + /** + * Whether to parse general section or not. + * This section contains very important data like beatmap mode or file format + * and should not be omitted unless you really need to. Different beatmap file formats + * can significantly affect beatmap parsing and difficulty & performance calculations. + */ + parseGeneral?: boolean; + /** + * Whether to parse editor section or not. + * This section isn't required anywhere so it can be disabled safely. + */ + parseEditor?: boolean; + /** + * Whether to parse metadata section or not. + * This section isn't required anywhere so it can be disabled safely. + */ + parseMetadata?: boolean; + /** + * Whether to parse difficulty section or not. + * This section is required for hit object processing and difficulty & performance calculations. + */ + parseDifficulty?: boolean; + /** + * Whether to parse events section or not. + * Events section contains information about breaks, background and storyboard. + * Changing this will also affects storyboard parsing. + */ + parseEvents?: boolean; + /** + * Whether to parse timing point section or not. + * Timing points are required for internal hit object processing. + */ + parseTimingPoints?: boolean; + /** + * Whether to parse hit object section or not. + * If you don't need hit objects you can safely disable this section. + */ + parseHitObjects?: boolean; + /** + * Whether to parse storyboard or not. + * If you don't need storyboard you can safely disable this section. + */ + parseStoryboard?: boolean; +} + +/** + * A basic decoder for readable file formats. + */ +declare abstract class Decoder { + /** + * Tries to read a file by its path in file system. + * @param path A path to the file. + * @throws If file doesn't exist or it can't be read. + * @returns A file buffer. + */ + protected _getFileBuffer(path: string): Promise; + /** + * Tries to get last update date of a file by its path in file system. + * @param path A path to the file. + * @throws If file can't be read. + * @returns Last file update date. + */ + protected _getFileUpdateDate(path: string): Promise; +} + +declare class SectionMap extends Map { + /** + * Current section of this map. + */ + currentSection: Section | null; + /** + * Gets the current state of the specified section. + * @param section Section name. + * @returns Current state of the section. + */ + get(section: Section): boolean; + /** + * Sets the state of the specified section. + * @param section Section name. + * @param state State of the section. + * @returns Reference to this section map. + */ + set(section: Section, state?: boolean): this; + /** + * Resets all section states to enabled and removes current section. + * @returns Reference to this section map. + */ + reset(): this; + get hasEnabledSections(): boolean; + /** + * Check if current section is enabled and should be parsed. + * Unknown sections are disabled by default. + * @returns If this section is enabled. + */ + get isSectionEnabled(): boolean; +} + +/** + * A decoder for human-readable file formats that consist of sections. + */ +declare abstract class SectionDecoder extends Decoder { + /** + * Current data lines. + */ + protected _lines: string[] | null; + /** + * Section map of this decoder. + */ + protected _sectionMap: SectionMap; + protected _getLines(data: any[]): string[]; + protected _parseLine(line: string, output: T): LineType; + protected _parseSectionData(line: string, output: T): void; + protected _preprocessLine(line: string): string; + protected _shouldSkipLine(line: string): boolean; + protected _stripComments(line: string): string; + protected _reset(): void; + /** + * Sets current enabled sections. + * All known sections are enabled by default. + * @param options Parsing options. + */ + protected _setEnabledSections(options?: IParsingOptions): void; +} + +declare type BufferLike = ArrayBufferLike | Uint8Array; + +/** + * A beatmap decoder. + */ +declare class BeatmapDecoder extends SectionDecoder { + /** + * An offset which needs to be applied to old beatmaps (v4 and lower) + * to correct timing changes that were applied at a game client level. + */ + static readonly EARLY_VERSION_TIMING_OFFSET = 24; + /** + * Current offset for all time values. + */ + protected _offset: number; + /** + * Current storyboard lines. + */ + protected _sbLines: string[] | null; + /** + * Performs beatmap decoding from the specified .osu file. + * @param path A path to the .osu file. + * @param options Beatmap parsing options. + * Setting this to boolean will only affect storyboard parsing. + * All sections that weren't specified will be enabled by default. + * @throws If file doesn't exist or can't be decoded. + * @returns A decoded beatmap. + */ + decodeFromPath(path: string, options?: boolean | IBeatmapParsingOptions): Promise; + /** + * Performs beatmap decoding from a data buffer. + * @param data The buffer with beatmap data. + * @param options Beatmap parsing options. + * Setting this to boolean will only affect storyboard parsing. + * All sections that weren't specified will be enabled by default. + * @throws If beatmap data can't be decoded. + * @returns A decoded beatmap. + */ + decodeFromBuffer(data: BufferLike, options?: boolean | IBeatmapParsingOptions): Beatmap; + /** + * Performs beatmap decoding from a string. + * @param str The string with beatmap data. + * @param options Beatmap parsing options. + * Setting this to boolean will only affect storyboard parsing. + * All sections that weren't specified will be enabled by default. + * @throws If beatmap data can't be decoded. + * @returns A decoded beatmap. + */ + decodeFromString(str: string, options?: boolean | IBeatmapParsingOptions): Beatmap; + /** + * Performs beatmap decoding from a string array. + * @param data The array of split lines. + * @param options Beatmap parsing options. + * Setting this to boolean will only affect storyboard parsing. + * @throws If beatmap data can't be decoded. + * @returns A decoded beatmap. + */ + decodeFromLines(data: string[], options?: boolean | IBeatmapParsingOptions): Beatmap; + protected _parseLine(line: string, beatmap: Beatmap): LineType; + protected _parseSectionData(line: string, beatmap: Beatmap): void; + /** + * Sets current enabled sections. + * All known sections are enabled by default. + * @param options Parsing options. + */ + protected _setEnabledSections(options?: IBeatmapParsingOptions): void; + protected _shouldParseStoryboard(options?: boolean | IBeatmapParsingOptions): boolean; +} + +/** + * A storyboard decoder. + */ +declare class StoryboardDecoder extends SectionDecoder { + /** + * Current section name. + */ + private _variables; + /** + * Performs storyboard decoding from the specified `.osu` or `.osb` file. + * If two paths were specified, storyboard decoder will try to combine storyboards. + * + * NOTE: Commands from the `.osb` file take precedence over those + * from the `.osu` file within the layers, as if the commands + * from the `.osb` were appended to the end of the `.osu` commands. + * @param firstPath The path to the main storyboard (`.osu` or `.osb` file). + * @param secondPath The path to the secondary storyboard (`.osb` file). + * @throws If file doesn't exist or can't be decoded. + * @returns A decoded storyboard. + */ + decodeFromPath(firstPath: string, secondPath?: string): Promise; + /** + * Performs storyboard decoding from a data buffer. + * If two data buffers were specified, storyboard decoder will try to combine storyboards. + * + * NOTE: Commands from the `.osb` file take precedence over those + * from the `.osu` file within the layers, as if the commands + * from the `.osb` were appended to the end of the `.osu` commands. + * @param firstBuffer The buffer with the main storyboard data (from `.osu` or `.osb` file). + * @param secondBuffer The buffer with the secondary storyboard data (from `.osb` file). + * @throws If storyboard data can't be decoded. + * @returns A decoded storyboard. + */ + decodeFromBuffer(firstBuffer: BufferLike, secondBuffer?: BufferLike): Storyboard; + /** + * Performs storyboard decoding from a string. + * If two strings were specified, storyboard decoder will try to combine storyboards. + * + * NOTE: Commands from the `.osb` file take precedence over those + * from the `.osu` file within the layers, as if the commands + * from the `.osb` were appended to the end of the `.osu` commands. + * @param firstString The string with the main storyboard data (from `.osu` or `.osb` file). + * @param secondString The string with the secondary storyboard data (from `.osb` file). + * @throws If storyboard data can't be decoded. + * @returns A decoded storyboard. + */ + decodeFromString(firstString: string, secondString?: string): Storyboard; + /** + * Performs storyboard decoding from a string array. + * If two string arrays were specified, storyboard decoder will try to combine storyboards. + * + * NOTE: Commands from the `.osb` file take precedence over those + * from the `.osu` file within the layers, as if the commands + * from the `.osb` were appended to the end of the `.osu` commands. + * @param firstData The string array with the main storyboard data (from `.osu` or `.osb` file). + * @param secondData The string array with the secondary storyboard data (from `.osb` file). + * @throws If storyboard data can't be decoded. + * @returns A decoded storyboard. + */ + decodeFromLines(firstData: string[], secondData?: string[]): Storyboard; + protected _parseLine(line: string, storyboard: Storyboard): LineType; + protected _parseSectionData(line: string, storyboard: Storyboard): void; + /** + * Sets current enabled sections. + * All known sections are enabled by default. + */ + protected _setEnabledSections(): void; + protected _preprocessLine(line: string): string; + protected _reset(): void; +} + +/** + * A score decoder. + */ +declare class ScoreDecoder extends Decoder { + /** + * Performs score decoding from the specified .osr file. + * @param path A path to the .osr file. + * @param parseReplay Should replay be parsed? + * @throws If file doesn't exist or can't be decoded. + * @returns A decoded score. + */ + decodeFromPath(path: string, parseReplay?: boolean): Promise; + /** + * Performs score decoding from a buffer. + * @param buffer The buffer with score data. + * @param parseReplay Should replay be parsed? + * @returns A decoded score. + */ + decodeFromBuffer(buffer: BufferLike, parseReplay?: boolean): Promise; + private _parseScoreId; +} + +/** + * A beatmap encoder. + */ +declare class BeatmapEncoder { + /** + * First playable lazer version. + */ + static readonly FIRST_LAZER_VERSION = 128; + /** + * Performs beatmap encoding to the specified path. + * @param path The path for writing the .osu file. + * @param beatmap The beatmap for encoding. + * @throws If beatmap can't be encoded or file can't be written. + */ + encodeToPath(path: string, beatmap?: IBeatmap): Promise; + /** + * Performs beatmap encoding to a string. + * @param beatmap The beatmap for encoding. + * @returns The string with encoded beatmap data. + */ + encodeToString(beatmap?: IBeatmap): string; +} + +/** + * A storyboard encoder. + */ +declare class StoryboardEncoder { + /** + * Performs storyboard encoding to the specified path. + * @param path The path for writing the .osb file. + * @param storyboard The storyboard for encoding. + * @throws If storyboard can't be encoded or file can't be written. + */ + encodeToPath(path: string, storyboard?: Storyboard): Promise; + /** + * Performs storyboard encoding to a string. + * @param storyboard The storyboard for encoding. + * @returns The string with encoded storyboard data. + */ + encodeToString(storyboard?: Storyboard): string; +} + +/** + * A score encoder. + */ +declare class ScoreEncoder { + /** + * Default game version used if replay is not available. + * It's just the last available osu!lazer version at the moment. + */ + static DEFAULT_GAME_VERSION: number; + /** + * Performs score & replay encoding to the specified path. + * @param path The path for writing the .osr file. + * @param score The score for encoding. + * @param beatmap The beatmap of the replay. + * It is required if replay contains non-legacy frames. + * @throws If score can't be encoded + * @throws If beatmap wasn't provided for non-legacy replay. + * @throws If score can't be encoded or file can't be written. + */ + encodeToPath(path: string, score?: IScore, beatmap?: IBeatmap): Promise; + /** + * Performs score encoding to a buffer. + * @param score The score for encoding. + * @param beatmap The beatmap of the replay. + * It is required if replay contains non-legacy frames. + * @throws If beatmap wasn't provided for non-legacy replay. + * @returns The buffer with encoded score & replay data. + */ + encodeToBuffer(score?: IScore, beatmap?: IBeatmap): Promise; +} + +/** + * A hittable object. + */ +declare class HittableObject extends HitObject implements IHasCombo { + isNewCombo: boolean; + comboOffset: number; + /** + * Creates a copy of this parsed hit. + * Non-primitive properties will be copied via their own clone() method. + * @returns A copied parsed slider. + */ + clone(): this; +} + +/** + * A holdable object. + */ +declare class HoldableObject extends HitObject implements IHoldableObject { + /** + * The time at which the holdable object ends. + */ + endTime: number; + /** + * The samples to be played when each node of the holdable object is hit. + * 0: The first node. + * 1: The first repeat. + * 2: The second repeat. + * ... + * n-1: The last repeat. + * n: The last node. + */ + nodeSamples: HitSample[][]; + /** + * The duration of the holdable object. + */ + get duration(): number; + /** + * Creates a copy of this holdable object. + * Non-primitive properties will be copied via their own clone() method. + * @returns A copied holdable object. + */ + clone(): this; +} + +/** + * A parsed slidable object. + */ +declare class SlidableObject extends HitObject implements ISlidableObject, IHasCombo { + /** + * Scoring distance with a speed-adjusted beat length of 1 second + * (ie. the speed slider balls move through their track). + */ + static BASE_SCORING_DISTANCE: number; + /** + * The duration of this slidable object. + */ + get duration(): number; + /** + * The time at which the slidable object ends. + */ + get endTime(): number; + /** + * The amount of times the length of this slidable object spans. + */ + get spans(): number; + set spans(value: number); + /** + * The duration of a single span of this slidable object. + */ + get spanDuration(): number; + /** + * The positional length of a slidable object. + */ + get distance(): number; + set distance(value: number); + /** + * The amount of times a slidable object repeats. + */ + repeats: number; + /** + * Velocity of this slidable object. + */ + velocity: number; + /** + * The curve of a slidable object. + */ + path: SliderPath; + /** + * The last tick offset of slidable objects in osu!stable. + */ + legacyLastTickOffset: number; + /** + * The samples to be played when each node of the slidable object is hit. + * 0: The first node. + * 1: The first repeat. + * 2: The second repeat. + * ... + * n-1: The last repeat. + * n: The last node. + */ + nodeSamples: HitSample[][]; + isNewCombo: boolean; + comboOffset: number; + applyDefaultsToSelf(controlPoints: ControlPointInfo, difficulty: BeatmapDifficultySection): void; + /** + * Creates a copy of this parsed slider. + * Non-primitive properties will be copied via their own clone() method. + * @returns A copied parsed slider. + */ + clone(): this; +} + +/** + * A parsed spinnable object. + */ +declare class SpinnableObject extends HitObject implements ISpinnableObject, IHasCombo { + /** + * The time at which the spinnable object ends. + */ + endTime: number; + isNewCombo: boolean; + comboOffset: number; + /** + * The duration of this spinnable object. + */ + get duration(): number; + /** + * Creates a copy of this parsed spinner. + * Non-primitive properties will be copied via their own clone() method. + * @returns A copied parsed spinner. + */ + clone(): this; +} + +export { BeatmapDecoder, BeatmapEncoder, HittableObject, HoldableObject, ScoreDecoder, ScoreEncoder, SlidableObject, SpinnableObject, StoryboardDecoder, StoryboardEncoder }; diff --git a/nise-replay-viewer/osu-parsers/package.json b/nise-replay-viewer/osu-parsers/package.json new file mode 100644 index 0000000..ea2c7d7 --- /dev/null +++ b/nise-replay-viewer/osu-parsers/package.json @@ -0,0 +1,65 @@ +{ + "name": "osu-parsers", + "version": "4.1.3", + "description": "A bundle of parsers for osu! file formats based on the osu!lazer source code.", + "exports": { + "node": { + "import": "./lib/node.mjs", + "require": "./lib/node.cjs" + }, + "import": "./lib/browser.mjs" + }, + "types": "./lib/index.d.ts", + "scripts": { + "build": "rollup -c && npm run format", + "test": "jest --verbose", + "fix": "eslint --fix ./src", + "format": "eslint --fix ./lib/** --no-ignore", + "prepublishOnly": "npm run build", + "docs": "npx typedoc" + }, + "keywords": [ + "osu", + "beatmap", + "storyboard", + "score", + "replay", + "parser", + "osu!std", + "osu!taiko", + "osu!mania", + "osu!catch", + ".osu", + ".osb", + ".osr" + ], + "author": "Kionell", + "repository": { + "type": "git", + "url": "https://github.com/kionell/osu-parsers" + }, + "license": "MIT", + "devDependencies": { + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-typescript": "^8.2.1", + "@types/jest": "^26.0.22", + "@types/node": "^14.18.12", + "@typescript-eslint/eslint-plugin": "^4.20.0", + "@typescript-eslint/parser": "^4.21.0", + "eslint": "^7.23.0", + "eslint-plugin-import": "^2.22.1", + "jest": "^26.6.3", + "rollup": "^2.44.0", + "rollup-plugin-dts": "^3.0.1", + "rollup-plugin-node-externals": "^3.1.2", + "typedoc": "^0.22.10", + "typedoc-plugin-missing-exports": "^1.0.0", + "typescript": "^4.1.5" + }, + "dependencies": { + "lzma-js-simple-v2": "^1.2.2" + }, + "peerDependencies": { + "osu-classes": "^3.0.0" + } +} diff --git a/nise-replay-viewer/package-lock.json b/nise-replay-viewer/package-lock.json new file mode 100644 index 0000000..40bc142 --- /dev/null +++ b/nise-replay-viewer/package-lock.json @@ -0,0 +1,3898 @@ +{ + "name": "sample-p5-editor", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sample-p5-editor", + "version": "0.0.0", + "dependencies": { + "@osujs/core": "^0.0.6", + "@osujs/math": "^0.0.4", + "@radix-ui/react-context-menu": "^2.1.5", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-menubar": "^1.0.4", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-slot": "^1.0.2", + "@types/react": "^18.2.37", + "@types/react-dom": "^18.2.15", + "@vitejs/plugin-react": "^4.2.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.0.0", + "eventemitter3": "^5.0.1", + "events": "^3.3.0", + "lucide-react": "^0.292.0", + "next-themes": "^0.2.1", + "osu-classes": "^3.0.1", + "osu-parsers": "^4.1.6", + "osu-standard-stable": "^5.0.0", + "p5": "^1.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-dropzone": "^14.2.3", + "react-router-dom": "^6.22.2", + "recharts": "^2.10.4", + "sonner": "^1.3.1", + "tailwind-merge": "^2.0.0", + "tailwindcss-animate": "^1.0.7", + "ts-md5": "^1.3.1", + "zustand": "^4.4.1" + }, + "devDependencies": { + "@types/events": "^3.0.3", + "@types/p5": "^0.9.1", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.31", + "tailwindcss": "^3.3.5", + "typescript": "^4.2.3", + "vite": "^4.4.5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", + "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", + "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.3", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.3", + "@babel/types": "^7.23.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", + "dependencies": { + "@babel/types": "^7.23.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.3.tgz", + "integrity": "sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==", + "dependencies": { + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.4.tgz", + "integrity": "sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==", + "dependencies": { + "@floating-ui/core": "^1.5.3", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.6.tgz", + "integrity": "sha512-IB8aCRFxr8nFkdYZgH+Otd9EVQPJoynxeFRGTB8voPoZMRWo8XjYuCRgpI1btvuKY69XMiLnW+ym7zoBHM90Rw==", + "dependencies": { + "@floating-ui/dom": "^1.5.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@next/env": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", + "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==", + "peer": true + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", + "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", + "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@osujs/core": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@osujs/core/-/core-0.0.6.tgz", + "integrity": "sha512-hnxdcjcMylS95NMtoAYwZZxFMsDkZDKAJk2nK6OCHwPXLaaW4DoVBdBFEoWy1VuuR9yAP8b1yATZtsLeaCH2mA==", + "peerDependencies": { + "@osujs/math": "0.0.4", + "utility-types": "^3.10.0" + } + }, + "node_modules/@osujs/math": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@osujs/math/-/math-0.0.4.tgz", + "integrity": "sha512-4zIvKjsS2ONVQIifRoJLT0BH3YldkOW88AG5yOZ8thjRG9Btdd1l9syAQWSDUchp7t3S9Ducquhctyv2v5lwiA==" + }, + "node_modules/@radix-ui/number": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", + "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", + "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.1.5.tgz", + "integrity": "sha512-R5XaDj06Xul1KGb+WP8qiOh7tKJNz2durpLBXAGZjSVtctcRFCuEvy2gtMwRJGePwQQE5nV77gs4FwRi8T+r2g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-menu": "2.0.6", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz", + "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", + "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.0.4.tgz", + "integrity": "sha512-bHgUo9gayKZfaQcWSSLr++LyS0rgh+MvD89DE4fJ6TkGHvjHgPaBZf44hdka7ogOxIOdj9163J+5xL2Dn4qzzg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-menu": "2.0.6", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", + "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", + "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.1.2.tgz", + "integrity": "sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", + "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", + "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", + "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@remix-run/router": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz", + "integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz", + "integrity": "sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", + "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", + "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz", + "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, + "node_modules/@types/events": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", + "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", + "optional": true, + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/p5": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@types/p5/-/p5-0.9.1.tgz", + "integrity": "sha512-4glOKdqdBiRWDFZwi/MjHudPV2U4t2L4fTTSacGapfFxyNXzZcAshAjqmrJkCIZcFlhRBEAL7AM95xRDMfrIwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.10", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", + "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" + }, + "node_modules/@types/react": { + "version": "18.2.37", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", + "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.15", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.15.tgz", + "integrity": "sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", + "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.0.tgz", + "integrity": "sha512-+MHTH/e6H12kRp5HUkzOGqPMksezRMmW+TNzlh/QXfI8rRf6l2Z2yH/v12no1UvTwhZgEDMuQ7g7rrfMseU6FQ==", + "dependencies": { + "@babel/core": "^7.23.3", + "@babel/plugin-transform-react-jsx-self": "^7.23.3", + "@babel/plugin-transform-react-jsx-source": "^7.23.3", + "@types/babel__core": "^7.20.4", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/aria-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", + "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.16", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "peer": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", + "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", + "dependencies": { + "clsx": "2.0.0" + }, + "funding": { + "url": "https://joebell.co.uk" + } + }, + "node_modules/class-variance-authority/node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "peer": true + }, + "node_modules/clsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "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/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.588", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.588.tgz", + "integrity": "sha512-soytjxwbgcCu7nh5Pf4S2/4wa6UIu+A3p03U2yVr53qGxi1/VTR3ENI+p50v+UxqqZAfl48j3z55ud7VHIOr9w==" + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-selector": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz", + "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "peer": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.292.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.292.0.tgz", + "integrity": "sha512-rRgUkpEHWpa5VCT66YscInCQmQuPCB1RFRzkkxMxg4b+jaL0V12E3riWWR2Sh5OIiUhCwGW/ZExuEO4Az32E6Q==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/lzma-js-simple-v2": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/lzma-js-simple-v2/-/lzma-js-simple-v2-1.2.2.tgz", + "integrity": "sha512-dHjXHlgcS8r2K+4t8kAycGE4xMVQxPzMTCrrYhaFEtQDmjXh/A7xq4D88oOfEtjTR9XFzTAML9AuBrm4jXLigA==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "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/next": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", + "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", + "peer": true, + "dependencies": { + "@next/env": "14.1.0", + "@swc/helpers": "0.5.2", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.1.0", + "@next/swc-darwin-x64": "14.1.0", + "@next/swc-linux-arm64-gnu": "14.1.0", + "@next/swc-linux-arm64-musl": "14.1.0", + "@next/swc-linux-x64-gnu": "14.1.0", + "@next/swc-linux-x64-musl": "14.1.0", + "@next/swc-win32-arm64-msvc": "14.1.0", + "@next/swc-win32-ia32-msvc": "14.1.0", + "@next/swc-win32-x64-msvc": "14.1.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-themes": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", + "integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==", + "peerDependencies": { + "next": "*", + "react": "*", + "react-dom": "*" + } + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/osu-classes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/osu-classes/-/osu-classes-3.0.1.tgz", + "integrity": "sha512-EI0pMoyUAgrvGHeOywaglHaGpjxhCHtwxPsK7kKJMDGmSgT8lvD3Ebs7vkSNPcKAuBX0VMbfsnSrwE9ArkOXyw==" + }, + "node_modules/osu-parsers": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/osu-parsers/-/osu-parsers-4.1.6.tgz", + "integrity": "sha512-k0PQaWBEZEMiweminI/A4j0lLTvsbHvhU/McMI94yJy+ME1ZfggAOp3vVee1P0Ze0Nu+pT/Ir7b0AlrhJO9IsQ==", + "dependencies": { + "lzma-js-simple-v2": "^1.2.2" + }, + "peerDependencies": { + "osu-classes": "^3.0.1" + } + }, + "node_modules/osu-standard-stable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/osu-standard-stable/-/osu-standard-stable-5.0.0.tgz", + "integrity": "sha512-qCzS1DFStHjgeDZguLRY5k4OgJSNXHG+sUdi1jm2Xwgk23QsyexwRQyTs5phFMuAnQmxeyjzWELLHOo99A/gMA==", + "peerDependencies": { + "osu-classes": "^3.0.0" + } + }, + "node_modules/p5": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/p5/-/p5-1.3.1.tgz", + "integrity": "sha512-g7W2htgEwiAEGcl0WHccAJKbunUJwrUojUSR9+KihphJ33p5VpDdh1K8pDx4ppYjOr/lVEXaZ1XXDj27nwlNOg==", + "license": "LGPL-2.1" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "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.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-dropzone": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz", + "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==", + "dependencies": { + "attr-accept": "^2.2.2", + "file-selector": "^0.6.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz", + "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-router": { + "version": "6.22.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz", + "integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==", + "dependencies": { + "@remix-run/router": "1.15.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz", + "integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==", + "dependencies": { + "@remix-run/router": "1.15.2", + "react-router": "6.22.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-smooth": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.5.tgz", + "integrity": "sha512-BMP2Ad42tD60h0JW6BFaib+RJuV5dsXJK9Baxiv/HlNFjvRLqA9xrNKxVWnUIZPQfzUwGXIlU/dSYLU+54YGQA==", + "dependencies": { + "fast-equals": "^5.0.0", + "react-transition-group": "2.9.0" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.10.4.tgz", + "integrity": "sha512-/Q7/wdf8bW91lN3NEeCjL9RWfaiXQViJFgdnas4Eix/I8B9HAI3tHHK/CW/zDfgRMh4fzW1zlfjoz1IAapLO1Q==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-smooth": "^2.0.5", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sonner": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.3.1.tgz", + "integrity": "sha512-+rOAO56b2eI3q5BtgljERSn2umRk63KFIvgb2ohbZ5X+Eb5u+a/7/0ZgswYqgBMg8dyl7n6OXd9KasA8QF9ToA==", + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "peer": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "peer": true, + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.0.tgz", + "integrity": "sha512-SqqhhaL0T06SW59+JVNfAqKdqLs0497esifRrZ7jOaefP3o64fdFNDMrAQWZFMxTLJPiHVjRLUywT8uFz1xNWQ==", + "dependencies": { + "@babel/runtime": "^7.23.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz", + "integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, + "node_modules/ts-md5": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz", + "integrity": "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "optional": true, + "peer": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.1.tgz", + "integrity": "sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/victory-vendor": { + "version": "36.8.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.8.2.tgz", + "integrity": "sha512-NfSQi7ISCdBbDpn3b6rg+8RpFZmWIM9mcks48BbogHE2F6h1XKdA34oiCKP5hP1OGvTotDRzsexiJKzrK4Exuw==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/zustand": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz", + "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/nise-replay-viewer/package.json b/nise-replay-viewer/package.json new file mode 100644 index 0000000..86af9af --- /dev/null +++ b/nise-replay-viewer/package.json @@ -0,0 +1,52 @@ +{ + "name": "sample-p5-editor", + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "tsc": "tsc" + }, + "devDependencies": { + "@types/events": "^3.0.3", + "@types/p5": "^0.9.1", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.31", + "tailwindcss": "^3.3.5", + "typescript": "^4.2.3", + "vite": "^4.4.5" + }, + "dependencies": { + "@osujs/core": "^0.0.6", + "@osujs/math": "^0.0.4", + "@radix-ui/react-context-menu": "^2.1.5", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-menubar": "^1.0.4", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-slot": "^1.0.2", + "@types/react": "^18.2.37", + "@types/react-dom": "^18.2.15", + "@vitejs/plugin-react": "^4.2.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.0.0", + "eventemitter3": "^5.0.1", + "events": "^3.3.0", + "lucide-react": "^0.292.0", + "next-themes": "^0.2.1", + "osu-classes": "^3.0.1", + "osu-parsers": "^4.1.6", + "osu-standard-stable": "^5.0.0", + "p5": "^1.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-dropzone": "^14.2.3", + "react-router-dom": "^6.22.2", + "recharts": "^2.10.4", + "sonner": "^1.3.1", + "tailwind-merge": "^2.0.0", + "tailwindcss-animate": "^1.0.7", + "ts-md5": "^1.3.1", + "zustand": "^4.4.1" + } +} diff --git a/nise-replay-viewer/postcss.config.js b/nise-replay-viewer/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/nise-replay-viewer/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/nise-replay-viewer/public/ctrl.svg b/nise-replay-viewer/public/ctrl.svg new file mode 100644 index 0000000..f081ae0 --- /dev/null +++ b/nise-replay-viewer/public/ctrl.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/nise-replay-viewer/public/cursor.png b/nise-replay-viewer/public/cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..de5198e6993d7cbffe494c420e2ade2b7a9d4852 GIT binary patch literal 9632 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 b0)U?Z7{#JgeFxQu00000NkvXXu0mjf#ihd! literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/default3.png b/nise-replay-viewer/public/default3.png new file mode 100644 index 0000000000000000000000000000000000000000..4da4f9ba5392dc63e1ee42ebc1264525eb657cf4 GIT binary patch literal 2907 zcmV-h3#9akP)3M6Z-N!gNo1mliDI~M%p`5VcQfM)sb<7?<%x+8Jm@47 zod+o|BtC%NhiNj%^y0m0;z)oyxy%)cLO~D;ZTXkUGHXxv^g!w9Ic;I)e92Bx_Bn08 zv-VzVeQRw%g$flaRH#s)LWK$yDpaUYp+X8$6l4qUl?4EqqTlQtYyvL<`;=04(7yy8e0DzxUn`{Cz zY2dQWMDi|PyjUrkwMQ^EHa5!<#~!{c9N4R0cE3O+P$pCYWkMxTCR74tLM2cpR03r} zB~T{h2GYwkR`B)h+qZrI7!l2i1J0d0SCNsCksj@xS65fpJ32Z>D1{4=Diah&%kz@u$(FJ}Aw6)5p6H~41mfpHD=TB22N|?%D`@HBt5Jb)WtUzIE%?ckS)%n4V1l zzycM{Qz#_T(zIpKg$hE+Oe7j;!#r-0APA-1-QA~jI-OR^jnHY(k4E|)CfWlFx!xB8 z=Y=;#H&FnIj03le^}ZS!8ai85Rb>**juNQz4Gs=Yy?*`r&q ztXE%OUm=<)0~(Dc-DZlamv*EF}kL&YUU7ZGs$41v#!G zBmgV2`VvJTE8^hm{N1~Ey}0>{X2k)x$8sbdA#YtoudI;xEF+&C=3ReR+9#o)lR> z92n;4_4;qQlIAcKnm&mpM~OsdBJ(6S;nK;)y%7HsUp*erJk?V$V^|Bjj&;IjyIiia z3l}a_NX=Xjc#X^Ds&F_Q7$f@Wa~zUGU@{bGD_4lejXjr$T_gh6Y4Fg{($A)S%-Gx8 zTNz6`F=vI$`UU}DXq$%?6_Oz)q)<@1&b%^NrI*GOjTvst(^#dk(%IR$c=YJel6Xa5w;C zN~xAuTU(pYS#&mu95F59WDp2M;vDnHT%5QQ0(3NCftEc}R?;xti$2( zie~MCOP4O`*!dQ2NXC9`uw>-1UcUexqKO*0xw+|le!f>N@E}R}hsgoRJ{iOz#EoUI z*XtL}h=7uk5-mjo>GU}d?mT;E#KVc;2gxC_476Eik!KBZa(hU0r8I2Pz_e^L5D3H+ z$H|3K1g3Il&~!wcE{wp{u%o6wiSDuwA(uc#8Y4^>Mxd5!yjf4>+5*O{4_U_(QLhr3QMlfW!4j?AvAs>&D5hyZGc|4)N_ zA~O+Q-DSjOQ%?KE0-a80zTIxO96NT*#MBeaL8*gZ$CV!aTxKE#8p=!#u@J;$GD$k8 zJUctP&hMeImOW!3kfl<*7=WY|Ha0dE9Y22Dv|UyZri3gKc}#tgS}7~wR)Maqt*t<+ z%+=oBo)imA<(7_75QLiBw{M$kYio-N3JP|$0FWiU5ik14^71kxJkuA3k_Io^>gSJX+zsd-p1BHrsUAuQa5{_#^aK8;Gz%t{twV zt*xzGYGEFQTdtowASa}XWeEql0+D}scz8<67hpvmI(h&^UN$)uKZQF%YFY=f`b^H- zA7)nA)YMek($Z2ZdXrty+1WY8JuF#`@v#sXqBqB}*x%njDb)fv62B*6awt=YhCsB! zJnY=RdGltC)V&Xv@8idhXE+@&lGX2uSP10bcn~?$Qc8((wkVK!)r+fJD;Ye$1 ztB(63YxIyl)8%Pg(vOyEKs8$GSviKTuC7sv00Q(XtJNb`Ngp@uDw5_aTrO9g)W%32 zXcaGByqN6i=|SrVg;~TG3v@c2y0d4`=3y&bEcFne_bDqY`zrwalQN+px##6#CD1@& zOclk1R-eyzJ{nIP4R~cGGcz+?sr%lzaihPzy?v0Yh5RVD4LH`C$THP<(JI;@)Q*nWnoi~Xm*kic4lshNc1Unc0{g{ zF%@GTOafR3zsKVl<2v`JlstH&L|r&U&M1TSlgvkt9?d0tIfs$= zjvnwhEnQjtFg%nXaFeU$G7=ATcXx*Zfxu#2U0psFvqe|M5x4@XtE>M$F)=a4k@t?Q zFg%Tv1*JL@*1`(nC=qQ83=FLI_xJk?3k%n=^;UG1!a%F*=;(OU+}!+zmgyLGX3iUr zGjmF|cC1lAxC->B-EKzO z7`JTS$Lqjhyqb%OImN_43W|XwumUL(*iP8yc*7i(I_GGavJQQw^27!4?9O6>X91}v z`J_hYG7(k~gq*8aujW-&R_e`Wvo1F`SG!Z1hOt~I6vE!Y1(f4kTU)1zm>`k1L1D3^4E+qf}002ovPDHLk FV1iXHNr(Ud literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/default4.png b/nise-replay-viewer/public/default4.png new file mode 100644 index 0000000000000000000000000000000000000000..061d48affe320dbca619fa352869ca94b63980ff GIT binary patch literal 1503 zcmV<51t9u~P)h3Hma;cPy+1+qpHZVzP|qa=;-L$3TzXO zzIN5flFQ}xlgZ={(hfuLTVRYLOQ#*0S65fRa2r|h)Eyfe+mdz|gr5TIC@{B>#CsT7 zhK7b*ch#1cm$$On?5?!KAp8{AF5Dby#mJJ$WFEVXEE^jeFH))0&wk#|@B+UC+L0x0 zXFtob9=5i&#-vIIu*FTM)6eOK&|h+eIrt&)rqE!Zy!h?bOtEDH+@cbN1He8>EJ%e{RG?O5!Bcl?YRW!!ZF#;-1o8u2f!dJ;dmAe&D?8L^ z?2^QNsnAe#1!_eW94JVoQcvlcw<}!#PNm{2C{Qc1U~hv43f@qIVG9gJ<6%>+K&{9E z|L<+sN8cgEmrzlSK<&ta0|h0$4g2W(TPil3Y6NOU7VK>Z0|k4+5%@+bzJda^A`61O zjchi%Ljv<8Ff^@hDJlZBBMS}`WHOm;I^uq(SCn`m6^8_sInr8@Md)q376S82`X}Cz ze!zR2e#Zr^Cn-vMT;f(>i%w)I)7!vnW>7P+?vxSu53MJ$7vw05bR+O)i9F@Vf&&FY zZ{uG8_*g3PkK434aZ$1$=QoF%intNjNXxA*<*7y%x-I!ViMuZq_)EDX&*nmC{R9q6)<+@{rHMcT0|OPmgkQKx2iUL);;LGZW7kKuuVnCM-}B z7N`je)Wl7YKBEo$jK`asn|rxjF7Qb|iA3U_%0e6*I+NZk0ed3&orFiEC_g5}&0|k+ zVa%rh@CX1tqCwSqsmfoX+uPe;sm$oFtE>Ck#rN002ovPDHLk FV1gBTvE2Xw literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/default5.png b/nise-replay-viewer/public/default5.png new file mode 100644 index 0000000000000000000000000000000000000000..b9b1b87a53bd23f1df38d555919f6680022ad4aa GIT binary patch literal 3009 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 Dl^%z) literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/default6.png b/nise-replay-viewer/public/default6.png new file mode 100644 index 0000000000000000000000000000000000000000..fa1497058c2bc823bbca27454b1ff2dcac13903c GIT binary patch literal 2957 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 D5xs|& literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/default7.png b/nise-replay-viewer/public/default7.png new file mode 100644 index 0000000000000000000000000000000000000000..767046becb001a8bbef7c02bbf6fe8236a4c4160 GIT binary patch literal 1353 zcmV-P1-AN$P)9rThbM%0pB@s;0hlAt{nIZ2EG8$0~{@J zK&k|^O_fNXs0j%e*KWWPNh4Jgy(^8p20S}|c9y=6WLdE$%JSr4c0FS+7=~dOhG7_n zVHk#CRQSTMty=&(L$CTv1Hie?(U}r>4?qq;h9o8hA+HXg3g9@(F@GI;C7QfG01rrD z(h3v;U(=tJC7KYJBY8q%o+Ks(A@7uqHByDT0z0I6FJCMcf0~?}><*3cl;!2+qf)8# zD}XojbBLekgKL4}krL1S{r#PzqoYqlqdaAIcegGde-wdVhF)QmV1b&jKuuVnCM-}B z7N`je)Px0UqI)6zfgTs)d4GSuvA(|kM`)A-3WdVmo}QjND*sZYQuzqr6M%o|`BP}n zHJDh9c>>_anBNQMYPH(4OeWLzLdN0YVWqdXcN4(x0A2vt1MpjD;5C?7OsGn$87Gcc zW76V3Ze?YqPeq@DWx2M5DexsXkGj5PB9Gd<`ZF1T6y{ zJ6e%W(UfslJT&X80eDLVD$9d~C4Jl6rs{qG@K}gct*>6MH?rC6FErU&Pr1!v$$ClDm8z~=gT zRImG_`s&i$7g-(>RG^pjAwdOtSsxNopmu#17Z)GX>GZmE#x+tO5>lXceOp^wL49Xb zo{{>H0D-qjTo-_>c(*e?KHe7^bp@in`T6-*Brp*7&*gtZ4c`SCsjplvi}&h(le`nU z_A`Rd0&UdymP8uEXMx_=2ag3NqCR*mP`kdpy}hGyx%|#%eehIZn&d^UPq4VSC|@|A z*r*Sl3e3{t?B_?6`gV49UX@CvzohbvUb@KmCa^>L)fK3=+-JQ$cqXv9zV8MG z1|Eh+U4iTR;E_Nh^=)o$zL7q4Z@oTvBv89Pu~j)cJ9|j->XJyS_#n_oeJ!>st=9)n z1e&ZbSnCTepsf?PKwI@i-Y8ckZh?ue4>1cgQs4IWc5vo(ULRr>s9m4fs+^dZkQWW= zF6%>#0*%zyVym+1vOdHp&}4n`E?X@$&@yod{EC*WOxI^@t5StH1b$26#B(;u^&v)q zT{H*grlzKp*ZX{_4>1V56U=);H4f~RjS}h;Ek*g4h7!G`rt^Q zc732bY>+%}>VqSJ+Vx4BMK$Tn8+(0lAkavCEw(CSyY6eowm_5hIoqmK!KOf4^*Pxr zY8RUVZ+v~QDDZz#A1n&gu1{!pX-Aqfx&8XMyEb$tMZJVy{iwF1m>mX`t0=d zba#~cT5MH1-Ro!<(*kcvH)Iqd^CKf8iP|Vv#k9aq>1+SFwY9aUs$Yn{q&}xxl`7b{ zi*G;O)8MhRail^p=LNSSc`n@*#5+vgbRyuq={6|uBiw|dK@wm4ULPz7M3cOe2)98w zCxNGQZ&NSlz5tuw7w(egL>11Z3cZ%cFbu;m48t%C!!Qh^27vznz4WWf1GwL|00000 LNkvXXu0mjfnR$Xj literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/default8.png b/nise-replay-viewer/public/default8.png new file mode 100644 index 0000000000000000000000000000000000000000..b17fb12a507ec976f339a64aaa81e938e7fc303c GIT binary patch literal 3532 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 wPJIm;G-%MEL4yVj8Z>Coph1I%Zx;aYKV?2L8R + + + + diff --git a/nise-replay-viewer/public/fav.png b/nise-replay-viewer/public/fav.png new file mode 100644 index 0000000000000000000000000000000000000000..43617fbef0b5b15fd1ce939953b2aefb7ea59cc1 GIT binary patch literal 24396 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∋Env~6y~ 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Ν4PoPF)#8=`FykJ`zK9B!du?eD`9pS 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

lItdzBMyma4ohYaYrj#QP!c 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 PAyrq^QK^3XJoNtnHdEvQ literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/hitcircle.png b/nise-replay-viewer/public/hitcircle.png new file mode 100644 index 0000000000000000000000000000000000000000..cf68cf9f41c3c0c3156657c6ce51f41e5f23c1e2 GIT binary patch literal 11843 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 ndAa|8H9)uPwdO?a~Xeq@Tic3x#~6U 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∋Env~6y~ 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Ν4PoPF)#8=`FykJ`zK9B!du?eD`9pS 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 V9GZ@(*7^DY;GBt=hNJ#z{{Tdl1b6@d literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/mod_hr.png b/nise-replay-viewer/public/mod_hr.png new file mode 100644 index 0000000000000000000000000000000000000000..71b7129b0ff75cac71b22fdf63b479e3943440fa GIT binary patch literal 1386 zcmeAS@N?(olHy`uVBq!ia0vp^x zJsQt;X+GJh`FMxs!|hrRwrSnps(p8h_MI)-w>RtD+N5)Hqt1wihUS6wrX^r0L z6$Yo58y;U`bZm*y(ZxoG7a1R1XuN;E$=>ihk?meb&o>sMlsmkIjNk+j$*!bKC9awA;;Yv!Bu8FumDf8W1%( zOl@+U(&#w3!EsW9V{eUf4-i#5cUQY~Rk?Ilx^w_hg=>3-Yg@T%OPO0^iF-q_dv(4? zRla9so@Yg_XIYL{NtSnUmUm%>cYeB0c8YIivR_)Fe`{Q3L$v%_Q#V7jgJba4!+xYau)JzO|Ype_H9+Y%N5&J7BC z#RI#TxK?=ROFd9p(UIWj`X zh5OIH?(WZ<)Xv;pq1V6a^5Yk~`r4f={BuI2(_^OGyWhUt>4t>5$*WM0|IN!<sJY_VpD z*1llVaJ|IIbNa_48{GEE?*I0M!R(jtdzl%MO~uRS#APe&`*`(C?msSTSvUK)b6J-$ z#4Iz~Wp!_Q!9&YSk7769D1A`JzI5)iVr!pi$y4qfuq`)IpQC(4eObhz{SSA1TElix zebpn0`8y}`pUg-&$-ZN4L-evjmjGT#MY+%0wN^Ex`X{Z5^@~3`V+GfuJuTAGGeS;Z zk~h4fad2*ZL0rIA&&EfWued(j^hZ%$F1OseUHyaGl${gbrLX_!zar&_r1-uPZqxYL z$DZGH+G2EYey>R1`Dy>|{biP@FM56Mw&a7*)e>i(S-!kj?Jn+FG5f4y%sau!)*&Kq zb?4hTo_xA#ZiZFjwy0|B8S(REmW29j>p1dfRn^s%k?~XKODqib^F4pz#*{Bpv!=}x rl76)^(je8c)?mNW*&hkEuRqA2wYnLu;(C)6RA70!`njxgN@xNAv>#l{ literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/mod_ht.png b/nise-replay-viewer/public/mod_ht.png new file mode 100644 index 0000000000000000000000000000000000000000..4a6c0391e99eec19aea0f404593f333543ca3988 GIT binary patch literal 1381 zcmeAS@N?(olHy`uVBq!ia0vp^xYA~SVc`i7cgYzLT6~&e@hK&ppU7=K$yI{hYJ+an0DxGh-L;^qqXuck)i(!8Z+v zw)0Qh&Np?Nz|^e*Q?>|B*&;A`v(V&C!jm@%P1-0taf8T&^`aBjiuSJ+>suqhnh9FfecF{h zx|Q8}<(>Lv9R_7>hNW$W#m&Y=jV1+k<^{Fpc{LWfRhBuGR@vp&*=06aCAOKxb{WO? z8AbN#g$`-?j;XoMDLF1FInK%1E=ifLi5YH*>Fx<>9`UIjamk)>NnWu)l;|Ck;2j<3 z8wEtMeo--gkum;}Kr-4tA}Syv%0D79AUrZK97sk4hD8L00m<;7u&|)eu;5T285$fC z60$p9b_*~vLrQ}Df*BZ@m|563I5;`Ed3gEw`2~eU#Ka{erDWvff5GIpSNJuhE2PUoH%vr%9U%^Z{NH3@aeOcAHMwl^XJdszn>i@ za{$wAiKmNWNCji=!A|?hg);0P-am^h@?JObw_Q-5)54&7on_u>DTA$p#t*uK^Zu1`qTf9tePxI{*m1Q^B z1w&t7-WRBwT&{Ryy@Ft+PDSY1xwg)meJ=2Bjh~Ew)NpD&SMh4tAD9hGfcc>U@2*ta%j<~*XN6UqmJFk_R*cw7_p35 zaAK^Ci`;s@@c6mO`=#_Umn(>-ySu#mDeyZ(Vx8U1`<)-R{+@8ET>a4`g@v=sdge2{ zTkCTuOvqi&U7_^P<^B27v-j!8HLO+EdlJeeJ$3!G=O(KU-&k;^drIn!2b;e=`}1CW zL+CPLU75v7snYERGs<2 znB@BK+>xKl9kQ}s>hxsodm-nfa&*3O5!cDKh{uOiD#XRpwG$`GK5}s>v*Q#Ms5~;y z*4qX!x2><%-TRs9tn1^o z$mU7PA`YL{tI~JBF6(=DQ1V#tVHMK=N1K_^E{tbxxoxpZTR1=9@Ct=PzHUr@GkG@W zsm+jm^)hE2lYZpMiLJ>6Vl1*fneM;mZCj-2|B~!TKVVefR2WVmU#D Nm8Ywp%Q~loCIG=cCKLbw literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/mod_nc.png b/nise-replay-viewer/public/mod_nc.png new file mode 100644 index 0000000000000000000000000000000000000000..54cf8ff928da767a41c4c8363360f4f571484203 GIT binary patch literal 1386 zcmeAS@N?(olHy`uVBq!ia0vp^xVX<4cT=EipO@M2n4%EH*y8$oSAgV+cC>* zI}pt@-!k2N^K^@iQ!Um`wp=^OYRx38)f25&POx6uYqO-sW?`4@+;+R!ZT7QT?Ps(& zOmB9W+T=L7!EsW(=fVhB){}TzqCaElz9K-xPbVWz_{qZSRjfD zjEM@2jtq(dqKKf#@ZgB>;P9~Eu+ZSp5D-XmI`s<}ogpPbe!&ckOw25792}gS+&sK| z{QQDKB4Xkal2S5q^70Cbs=CIemR7cQegOf2!LhLknWbgbb@gr2r_WokYQv^oM^2nN zb>+&n>$mUSd-(L(%MV|E|M~Oh@88c3lR1D%x6ISUF{FaA^k92@PoWI^hxd1F)mJKO z?%Jdkc+=(EI*-CH69ZMcj8E%mFMTs{>cqXNsbUL{Jm>#hRky!-SNH3`V&3mLKTlVl zk;;Ai`=oa{%+D63fBtn_WkNCApRWG6Jv9$cxcxX=;>A#{rSmh?_ez{h9IN-L*gwD0 z4jjMjGAVbymFK5Q%^IU$UgF70HGJD9=D+G<%(45~mvfhSerMZ{8%f0r-o8zF{W0{y zr|kV3Yt6UJiI|;nF7txf)=$oyfA-A38N`x1D_Ep@5&N=7+Wt2-aW-z-_UYhOrx`Y# zWrnTi+KQ$=sVfQh+$4AEO@N@_@r(@l`cHQMzIMDld%?}xMJ35_impoegZNaH?gcu1 z0)}dit5c49{&^px{@rnQN=LTfmf$rG23OZKZablRQ7$7rvMXWRXBHFxEjz*;tk1?W zaB)4{o+#T;6y_ZLP^?^S3$x9(wLiuG%QP)*dsFdzZK~xKpY1OGjoT)gznlETH{#vI zCv%V4XD{^ao%TV{d*R-{UYDIC=3V;oSar)%A%TfJ? zpbMUt#UpNIyoz8>b*!1uVZqjWy7|+rW%qwx2n()cQ_a7l(B9$kVnbwr)JhS@+fGXv zPpwH@n6$~g?^Nqcv8l2bPOUIGFvVKyl3rJFhLz8~>bEMD0=EJu{i)l|vh2%3zl%A4 zCrxvx^qW+A;oO8L$r|31e%(BByuiP$Xtv6C$JGiaUk9k|=6!i7{fEqqYw=d`TV5(S`%Qzp%7!_50^J3X-RQ>COq911pcc-U^q_NoyLrqRY oMYEUZ3V2KS`HtCMNbNWuuikN8tNm5eD^P*u>FVdQ&MBb@0CXl_+yDRo literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/mod_nf.png b/nise-replay-viewer/public/mod_nf.png new file mode 100644 index 0000000000000000000000000000000000000000..1fe3902f123b0e89cd31f2acb9a3286f7d0c4ee1 GIT binary patch literal 1222 zcmeAS@N?(olHy`uVBq!ia0vp^x~-3+kT{S5)HP!t!@?6F?vgVgwD>f`;!{u(%w=43ifPeFriCY%7anI` zaFlgE5FKHge}r}35w^L9*ykMJn6saA<{q9IyLhMX;G4FMf9h6&DO&_4Z4{okL1e-@ z(cV=OJu4);mP&Umk?CA4+p$=-eW6_Y0{OQ23T^Y{+vX{>&QoZbrQA3}rE$7y!!)(} zDe85THEJekRdnf6S>;w*hCVI!j`$Pj#oNr`|e`JhbWVC-o zR6uw{U|4uiSXfYKSa4`)a7akVhG2su!0-+!3GxeOU}R!uVdLQ7TiU6cP~= zmync_k&~BKP*l}5Hnp^}wet%I2n>#mO~@=QtFEhWn?8Nsf>j$f?K*Pe)Tt|1u3f)< z@7}|w&t87`^83%9KY#yzc9_h;z`$hW>EaktajQ2ZJzO|Ypw_eOCBAY zrQg{%H!j-V_Skc?Os?yxf9$ONhW5*kF}0W9cy!}aV}{#a@jEU@oK|=LYHr>%F=u`M zG4cJ((eh!_!tB}_*Kc3+uxfYX+>7U%FRyR-y8H5(`8)s1MST0@Qt#HIWBU2V87l@~ zffeD*AEwzaJv-@M(m@IR%3e{O7d<>I{styt0=XgY9=xC5JbT-N_)qt*u~Z$KEW0SM zy0!h`PJ=T!myd6n$8~ST=Gpt>j1W$@rIIR56E%%DK zzRO>0ss97<9j;32>>lnv80vL3;789nyZ-nomtI%s7R>M7VSA{)J?l|GIK%UdyY?!LzbL=FhoxkmZzF#O&n$;BAXu zotx+xe07e@%QbqF9)}vM?%%VnaQjTtYg3;uzws|P^5oRjZ;XspuP-hC!LNMzCTmlD R>_`P?Dzlx literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/mod_nm.png b/nise-replay-viewer/public/mod_nm.png new file mode 100644 index 0000000000000000000000000000000000000000..fa9cb6db53f4cf7e34338a75b3db938024f22c14 GIT binary patch literal 1184 zcmeAS@N?(olHy`uVBq!ia0vp^x4o^WSE+oqA37!_4M>G1tB8V*4FIo?5toQBqYSo&kq!Lc6N4gae*jM zR8;f>0}n9J(a{0A!OF_Y($dnx!otVL$IQ$ODDUs@@95|VWVpJz1_lNO1qA_>14*Db zkn8K~>*eJIVF0xNl>w!I+z>Db4h{y=K=S_?#eYC_TJi5`rC%qNz8zEfdQ|1h5tYwJ zR6iY7{dh?A{Xw;N2h`r~S9`Nx{ncLeXS*~X@6f!zRr~H1?c1AmZf(-Juv-88YW=e- z4NfjIJif%}*b<|oi;a#fHa@VxWdD4Vz4J`>&NbaV$8_gxvmLX{w$C!#I>UVPG>i39 zELTmiS~^>_;Mia5)LQ1+RN_`&sg-TRhsQp0z_HfIjO!`DZZ%*{weYPNpS&*u>lD&0g>Us5#hn%VZmXc!J(lcph(qN=X3sil>zonJse zU~p_~LS|`MbzObi^y%{!tlF??*O3#au3Wix{r0_k51&4J`Qgj&KY#xI^?F?r&cMK= z?CIhdQgN$wN_cdzqd=SbNjXCYhKUU9OTH_41v+Rd?Ui`2xpC2r4dxq?9a%e*^ffNN zssEbfchlBi&M17h@%y7!`cG_{`@7#IMajCX#NSuz~>6LjSuzqHny_1phm)Gl~?i~V-&`|uY%jAh5>IcHU1G1L|4Le?8A+;NCA=F|XM2@ZO$~f`te3`0SYtzB{-3Ggchzd#4~6xS+AV zK8z~3_e`=4- zt%;A65@h`}YuVkkmmb7Vzq@u;-YISYL00cwQ+kievj3|Hym@2YB!&E(va>FivS+L~ zxy|pgMxK9F=G&v+ZvHk}?KUaqqetj6Wz9o5zKaCbE-UD_+qxw^`s+(g-_Wb-%gZvP zQ>9m({g{0!Eaa}G)zkpf5SeMS>#DE;rA{xBI=56B zq9!GP_uW2uAiA|q=JtA-)-1vPBH<0a;_JG_SGJ2i+ab5KMHDFee!u+Dd6GcJ#pTl9 zk12dRs_^r;!krB=K=R{3`L9P5UhS59xJ4Ey4rBmzz1=7O>jX&I|1*j}^zXFd-&0T$ z#087|Ii>Xbq|z@SI-&gYg!1Pjsvi!iemJQ1?tt2hJsQt;YdqPh`Ea||{cT$Jwrbzn zq;qY(?$!0W=U3^UU1@N7g~93Nh9{R99$jK|Xrb}Jg~og5nr@qEzGb@k=4loirdY0@ zV!3v*<=RPBYbIH(oM64Y&w6o>&B89*c^!6h+U#ex*iUY7Y%6zdDRXNqac?MguPbt| zE%d0$^DNKtD$erGP4mf2_RC1}ONsZ7j|q&64vdWsjEM@03=fV74-O9t4h;-j_Uf0`yZ}|R$I#OWr1_w-`yAW{7&5H5q4l&bW`=buEX(13vP=vu#}uvThF&b ziM`C9Stw<_Z`?wr1dFWiDjpm1Of8cFn3_!8e_dnhe6{_=?`td)s&g{Figvx~SF$`? z%_wk1x;IE-^K+YaFaD?}JMNpVe4VxR){4ruxk~466`s8Ej^zmd-o7U*svd8AZgop< z^2%Kdan40vlQnM#aoY(s1z()gb3tf+cbJ`|yyV#(9)$+|DF)m-Qhe2Buo>~#=*^t; zu_?gq^T{b^J~LP5zwuT7>>;%7c-!6#&vRMF`Xtvon|wUXmSbJx#VwIxe*+p_xPZF2MH-E&~R*!?uFFlpn1 zRY{_q(;v6!*8dV>lrs32RdU>Y-}#jnPnkC`c?hjsza!yG{Vv9UB}u6=-Tu1mpO*sJ zXJ&8h+9EV(BFo~3RYH@6aypatrf43NRJ*q(eSO=M1b^$kpgDra9R)Ui=Ig$>Cg(CU R(^OFA^mO%eS?83{1ONdH&p`kH literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/mod_sd.png b/nise-replay-viewer/public/mod_sd.png new file mode 100644 index 0000000000000000000000000000000000000000..b9a6d1e662c1ce2c86272951eed2e58959b6ed31 GIT binary patch literal 1291 zcmeAS@N?(olHy`uVBq!ia0vp^xa$teU-npiG=9+GwWww2$`IhPCo2FT8oNBRQiskw#ma8UM zt(;)Jtk-67kIkZP+XY>=3p#D*wAs&SahTTZFty2XN~7cC2FFSDP7~^!`fHtfYMgtj zox7@BIx1Znirwpr-0OHVSJTV3)|if+?ozyEv|y4#K%G!>3| zdr`esrQXgmj5~Lkcaz!_$B+A$eA*`Hb(7`dnHUZBeMT#`vwE%5G*jLuw5?6Q0EQz@%r3Gv-49rf->4YKUJsOxAWYqbC^_@u~bm#ZSys+ zPiM>C{CAwRywPkD`@)}#j_#JbCAq0wyg^uHKH~w~O_H~&)>oWfI9KUCgF`NVr5MAk zlgbxP?G@U~aEtMi7Q>sLu6Ao!3p#R|wi!enx~#n*=f}x+XB++t&z~Io^+4^vmCZpD z<@C}H)nw?Ocz12u`6u(@KHYvg`^oz%jnBe96F;szy|BdKfwEObc580ss|vxw#TU=+Sh7n zpHe-%W=W04?Y?;F1)o-Ndeu!*WjuDI=PF0m^0(LAG*%m{NhKNYt>nns@#Epk*NuJO z%vhdQ`d&N~@n=JPO%sY_ZsCuE=cGWEy)sh5}UIC9YIj805&Z@{YSB57(-k5YPi zy=PCfwVv`uGP5w5SMp>}$45Wjqte2OU2pX#{Qp$*>&JxWpc2K?)z4*}Q$iB}v|KUc literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/mouse.svg b/nise-replay-viewer/public/mouse.svg new file mode 100644 index 0000000..cacfb58 --- /dev/null +++ b/nise-replay-viewer/public/mouse.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/nise-replay-viewer/public/qkey.svg b/nise-replay-viewer/public/qkey.svg new file mode 100644 index 0000000..ead1eb1 --- /dev/null +++ b/nise-replay-viewer/public/qkey.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/nise-replay-viewer/public/radix.png b/nise-replay-viewer/public/radix.png new file mode 100644 index 0000000000000000000000000000000000000000..0c9f0dd908bdee52e93541d5c52ccf35192b9bc2 GIT binary patch literal 19462 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%0AfdmMXp6TI%zd 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 + + + + + + diff --git a/nise-replay-viewer/public/sliderb0.png b/nise-replay-viewer/public/sliderb0.png new file mode 100644 index 0000000000000000000000000000000000000000..0b08aa21b33bc7f7ceb6e841a3f213d07f334c87 GIT binary patch literal 7247 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 literal 0 HcmV?d00001 diff --git a/nise-replay-viewer/public/sliderfollowcircle.png b/nise-replay-viewer/public/sliderfollowcircle.png new file mode 100644 index 0000000000000000000000000000000000000000..f666b62bdd87407e63a30bc46ff3a85b69b729ad GIT binary patch literal 22541 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 WphU4R7reXi2UeDL7G + + + + diff --git a/nise-replay-viewer/public/vite.svg b/nise-replay-viewer/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/nise-replay-viewer/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/nise-replay-viewer/src/decorators/hook.ts b/nise-replay-viewer/src/decorators/hook.ts new file mode 100644 index 0000000..fd99bdc --- /dev/null +++ b/nise-replay-viewer/src/decorators/hook.ts @@ -0,0 +1,11 @@ +import { Events, hook } from "@/hooks"; + +export function Hook(event: Events): any { + return function ( + _target: any, + _propertyKey: string, + descriptor: PropertyDescriptor + ) { + hook({ event, callback: descriptor.value }); + }; +} diff --git a/nise-replay-viewer/src/decorators/singleton.ts b/nise-replay-viewer/src/decorators/singleton.ts new file mode 100644 index 0000000..fa83cd1 --- /dev/null +++ b/nise-replay-viewer/src/decorators/singleton.ts @@ -0,0 +1,14 @@ +export function Singleton any>(ctr: T): T { + let instance: T; + + return class { + constructor(...args: any[]) { + if (instance) { + return instance; + } + + instance = new ctr(...args); + return instance; + } + } as T; +} diff --git a/nise-replay-viewer/src/globals.css b/nise-replay-viewer/src/globals.css new file mode 100644 index 0000000..52a8d2d --- /dev/null +++ b/nise-replay-viewer/src/globals.css @@ -0,0 +1,76 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + + --radius: 0.5rem; + } + + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/nise-replay-viewer/src/hooks.ts b/nise-replay-viewer/src/hooks.ts new file mode 100644 index 0000000..f70058b --- /dev/null +++ b/nise-replay-viewer/src/hooks.ts @@ -0,0 +1,38 @@ +export interface Hook { + event: T; + callback: (...args: any) => boolean | void; +} + +export enum Events { + setup, + draw, + preload, + resize, + mousePressed, + mouseDragged, + mouseReleased, + mouseWheel, + keyPressed, + keyReleased, +} + +const hooks: Map[]> = new Map(); + +export function hook({ event, callback }: Hook) { + if (!hooks.get(event)) { + hooks.set(event, []); + } + hooks.set(event, [...hooks.get(event)!, { event, callback }]); +} + +export function hookable(event: Events) { + return (...args: any) => { + const hookList = hooks.get(event); + + if (!hookList?.length) return; + + for (const hook of hookList) { + hook.callback(...args); + } + }; +} diff --git a/nise-replay-viewer/src/hooks/canvasControls.ts b/nise-replay-viewer/src/hooks/canvasControls.ts new file mode 100644 index 0000000..8966401 --- /dev/null +++ b/nise-replay-viewer/src/hooks/canvasControls.ts @@ -0,0 +1,63 @@ +import { Hook } from "@/decorators/hook"; +import { Events } from "@/hooks"; +import { p } from "@/utils"; +import p5 from "p5"; + +export let canvasDragging: boolean; +export let canvasDragStart: p5.Vector; +export let canvasMultiplier: number; +export let canvasTranslation: p5.Vector; + +export class CanvasControlHooks { + @Hook(Events.setup) + static setupCanvasControls() { + canvasDragStart = p.createVector(0, 0); + canvasDragging = false; + canvasMultiplier = p.windowHeight / 384; + canvasTranslation = p.createVector(0, 0); + } + + @Hook(Events.mousePressed) + static mousePressed() { + if (p.mouseButton === p.CENTER) { + canvasDragging = true; + canvasDragStart = p.createVector(p.mouseX, p.mouseY); + } + } + + @Hook(Events.mouseDragged) + static mouseDragged() { + if (canvasDragging) { + const mousePos = p.createVector(p.mouseX, p.mouseY); + const delta = mousePos.copy().sub(canvasDragStart).div(canvasMultiplier); + canvasTranslation.add(delta); + canvasDragStart = mousePos; + } + } + + @Hook(Events.mouseReleased) + static mouseReleased() { + canvasDragging = false; + } + + @Hook(Events.mouseWheel) + static mouseWheel(event: any) { + if (!p.keyIsDown(17)) { + return; + } + const zoom = event.deltaY > 0 ? 100 : -100; + const oldScale = canvasMultiplier; + const multiplierChange = zoom * 0.0008 * oldScale; + canvasMultiplier -= multiplierChange; + + const newScale = canvasMultiplier; + const mousePos = p.createVector(p.mouseX, p.mouseY); + const mousePosScaled = mousePos.copy().div(oldScale); + const mousePosScaledNew = mousePos.copy().div(newScale); + const delta = mousePosScaledNew.copy().sub(mousePosScaled); + canvasTranslation.add(delta); + event.preventDefault(); + + return false; + } +} diff --git a/nise-replay-viewer/src/hooks/osuControls.ts b/nise-replay-viewer/src/hooks/osuControls.ts new file mode 100644 index 0000000..5081975 --- /dev/null +++ b/nise-replay-viewer/src/hooks/osuControls.ts @@ -0,0 +1,46 @@ +import { Hook } from "@/decorators/hook"; +import { Events } from "@/hooks"; +import { OsuRenderer } from "@/osu/OsuRenderer"; +import { p, state } from "@/utils"; + +export class OsuControlHooks { + @Hook(Events.keyPressed) + static keyPressed() { + if (p.keyCode == 32) OsuRenderer.setPlaying(!OsuRenderer.playing); + } + + @Hook(Events.mouseWheel) + static mouseWheel(event: WheelEvent) { + if (event.ctrlKey) return; + const currentState = state.getState(); + + if ( + currentState.achivementsDialog || + currentState.dataAnalysisDialog || + currentState.metadataEditorDialog || + currentState.openDialog || + currentState.saveDialog + ) + return; + + if (event.deltaY > 0) { + OsuRenderer.setTime(OsuRenderer.time + 35); + } else { + OsuRenderer.setTime(OsuRenderer.time - 35); + } + } + + @Hook(Events.mousePressed) + static mousePressed(event: WheelEvent) { + const currentState = state.getState(); + + if ( + currentState.achivementsDialog || + currentState.dataAnalysisDialog || + currentState.metadataEditorDialog || + currentState.openDialog || + currentState.saveDialog + ) + return; + } +} diff --git a/nise-replay-viewer/src/hooks/p5Base.ts b/nise-replay-viewer/src/hooks/p5Base.ts new file mode 100644 index 0000000..a63c866 --- /dev/null +++ b/nise-replay-viewer/src/hooks/p5Base.ts @@ -0,0 +1,47 @@ +import { Events } from "@/hooks"; +import { Renderer } from "@/renderer"; +import { p } from "@/utils"; +import { + canvasDragging, + canvasMultiplier, + canvasTranslation, +} from "./canvasControls"; +import { Hook } from "@/decorators/hook"; + +export class p5Hooks { + @Hook(Events.setup) + static setup() { + p.createCanvas(p.windowWidth, p.windowHeight); + p.imageMode(p.CENTER); + p.frameRate(120); + Renderer.setup(); + Renderer.mouse = p.createVector(); + } + + @Hook(Events.draw) + static draw() { + p.cursor("default"); + if (canvasDragging) { + p.cursor("grabbing"); + } + + p.background(0); + p.scale(canvasMultiplier); + p.translate(canvasTranslation.x, canvasTranslation.y); + + const translated = p.createVector(p.mouseX, p.mouseY); + translated.mult(1 / canvasMultiplier); + translated.sub(canvasTranslation); + Renderer.mouse.set(translated); + + Renderer.draw(); + + p.translate(-canvasTranslation.x, -canvasTranslation.y); + p.scale(1 / canvasMultiplier); + } + + @Hook(Events.resize) + static resize() { + p.resizeCanvas(p.windowWidth, p.windowHeight); + } +} diff --git a/nise-replay-viewer/src/hooks/starter.ts b/nise-replay-viewer/src/hooks/starter.ts new file mode 100644 index 0000000..01ca813 --- /dev/null +++ b/nise-replay-viewer/src/hooks/starter.ts @@ -0,0 +1,4 @@ +import "./canvasControls"; +import "./osuControls"; +import "./p5Base"; +import "./tooling"; diff --git a/nise-replay-viewer/src/hooks/tooling.ts b/nise-replay-viewer/src/hooks/tooling.ts new file mode 100644 index 0000000..77ef46e --- /dev/null +++ b/nise-replay-viewer/src/hooks/tooling.ts @@ -0,0 +1,29 @@ +import { Hook } from "@/decorators/hook"; +import { Events } from "@/hooks"; +import { Tool } from "@/tooling/tool"; + +export class Tooling { + static currentTool: Tool | undefined; + + @Hook(Events.mousePressed) + static mousePressed() { + if (!this.currentTool) return; + this.currentTool.mousePressed(); + } + + @Hook(Events.mousePressed) + static mouseReleased() { + if (!this.currentTool) return; + this.currentTool.mouseReleased(); + } + + @Hook(Events.draw) + static tick() { + if (!this.currentTool) return; + this.currentTool.tick(); + } + + static purge() { + this.currentTool = undefined; + } +} diff --git a/nise-replay-viewer/src/interface/App.tsx b/nise-replay-viewer/src/interface/App.tsx new file mode 100644 index 0000000..449d6d8 --- /dev/null +++ b/nise-replay-viewer/src/interface/App.tsx @@ -0,0 +1,38 @@ +import {AboutDialog} from "./composites/about-dialog"; +import {AnalysisSheet} from "./composites/analysis.-sheet"; +import {Navbar} from "./composites/Menu"; +import {SongSlider} from "./composites/song-slider"; +import {Helper} from "./composites/helper"; +import {useEffect, useState} from "react"; +import {OsuRenderer} from "@/osu/OsuRenderer"; + +export function App() { + + const [replayId, setReplayId] = useState(""); + + useEffect(() => { + let pathReplayId = location.pathname.slice(1, location.pathname.length); + + const loadReplay = async () => { + // TODO: Check for security? + await OsuRenderer.loadReplayFromUrl(`http://localhost:8080/score/${pathReplayId}/replay`); + OsuRenderer.setPlaying(true); + // await OsuRenderer.loadReplayFromUrl(`https://nise.moe/api/score/${pathReplayId}/replay`); + }; + + if(replayId !== pathReplayId) { + setReplayId(pathReplayId); + loadReplay(); + } + }, [location.pathname]); + + return ( + <> + + + + + + + ); +} diff --git a/nise-replay-viewer/src/interface/components/theme-provider.tsx b/nise-replay-viewer/src/interface/components/theme-provider.tsx new file mode 100644 index 0000000..7b9eeb2 --- /dev/null +++ b/nise-replay-viewer/src/interface/components/theme-provider.tsx @@ -0,0 +1,73 @@ +import { createContext, useContext, useEffect, useState } from "react"; + +type Theme = "dark" | "light" | "system"; + +type ThemeProviderProps = { + children: React.ReactNode; + defaultTheme?: Theme; + storageKey?: string; +}; + +type ThemeProviderState = { + theme: Theme; + setTheme: (theme: Theme) => void; +}; + +const initialState: ThemeProviderState = { + theme: "system", + setTheme: () => null, +}; + +const ThemeProviderContext = createContext(initialState); + +export function ThemeProvider({ + children, + defaultTheme = "system", + storageKey = "vite-ui-theme", + ...props +}: ThemeProviderProps) { + const [theme, setTheme] = useState( + () => (localStorage.getItem(storageKey) as Theme) || defaultTheme + ); + + useEffect(() => { + const root = window.document.documentElement; + + root.classList.remove("light", "dark"); + + if (theme === "system") { + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") + .matches + ? "dark" + : "light"; + + root.classList.add(systemTheme); + return; + } + + root.classList.add(theme); + }, [theme]); + + const value = { + theme, + setTheme: (theme: Theme) => { + localStorage.setItem(storageKey, theme); + setTheme(theme); + }, + }; + + return ( + + {children} + + ); +} + +export const useTheme = () => { + const context = useContext(ThemeProviderContext); + + if (context === undefined) + throw new Error("useTheme must be used within a ThemeProvider"); + + return context; +}; diff --git a/nise-replay-viewer/src/interface/components/ui/badge.tsx b/nise-replay-viewer/src/interface/components/ui/badge.tsx new file mode 100644 index 0000000..f000e3e --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +

+ ) +} + +export { Badge, badgeVariants } diff --git a/nise-replay-viewer/src/interface/components/ui/button.tsx b/nise-replay-viewer/src/interface/components/ui/button.tsx new file mode 100644 index 0000000..0ba4277 --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/nise-replay-viewer/src/interface/components/ui/card.tsx b/nise-replay-viewer/src/interface/components/ui/card.tsx new file mode 100644 index 0000000..afa13ec --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/card.tsx @@ -0,0 +1,79 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/nise-replay-viewer/src/interface/components/ui/context-menu.tsx b/nise-replay-viewer/src/interface/components/ui/context-menu.tsx new file mode 100644 index 0000000..3e52999 --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/context-menu.tsx @@ -0,0 +1,198 @@ +import * as React from "react" +import * as ContextMenuPrimitive from "@radix-ui/react-context-menu" +import { Check, ChevronRight, Circle } from "lucide-react" + +import { cn } from "@/lib/utils" + +const ContextMenu = ContextMenuPrimitive.Root + +const ContextMenuTrigger = ContextMenuPrimitive.Trigger + +const ContextMenuGroup = ContextMenuPrimitive.Group + +const ContextMenuPortal = ContextMenuPrimitive.Portal + +const ContextMenuSub = ContextMenuPrimitive.Sub + +const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup + +const ContextMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName + +const ContextMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName + +const ContextMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName + +const ContextMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName + +const ContextMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +ContextMenuCheckboxItem.displayName = + ContextMenuPrimitive.CheckboxItem.displayName + +const ContextMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName + +const ContextMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName + +const ContextMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName + +const ContextMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +ContextMenuShortcut.displayName = "ContextMenuShortcut" + +export { + ContextMenu, + ContextMenuTrigger, + ContextMenuContent, + ContextMenuItem, + ContextMenuCheckboxItem, + ContextMenuRadioItem, + ContextMenuLabel, + ContextMenuSeparator, + ContextMenuShortcut, + ContextMenuGroup, + ContextMenuPortal, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, + ContextMenuRadioGroup, +} diff --git a/nise-replay-viewer/src/interface/components/ui/dialog.tsx b/nise-replay-viewer/src/interface/components/ui/dialog.tsx new file mode 100644 index 0000000..c23630e --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/dialog.tsx @@ -0,0 +1,120 @@ +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/nise-replay-viewer/src/interface/components/ui/input.tsx b/nise-replay-viewer/src/interface/components/ui/input.tsx new file mode 100644 index 0000000..677d05f --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/nise-replay-viewer/src/interface/components/ui/label.tsx b/nise-replay-viewer/src/interface/components/ui/label.tsx new file mode 100644 index 0000000..683faa7 --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/label.tsx @@ -0,0 +1,24 @@ +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" +) + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)) +Label.displayName = LabelPrimitive.Root.displayName + +export { Label } diff --git a/nise-replay-viewer/src/interface/components/ui/menubar.tsx b/nise-replay-viewer/src/interface/components/ui/menubar.tsx new file mode 100644 index 0000000..d11c299 --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/menubar.tsx @@ -0,0 +1,234 @@ +import * as React from "react" +import * as MenubarPrimitive from "@radix-ui/react-menubar" +import { Check, ChevronRight, Circle } from "lucide-react" + +import { cn } from "@/lib/utils" + +const MenubarMenu = MenubarPrimitive.Menu + +const MenubarGroup = MenubarPrimitive.Group + +const MenubarPortal = MenubarPrimitive.Portal + +const MenubarSub = MenubarPrimitive.Sub + +const MenubarRadioGroup = MenubarPrimitive.RadioGroup + +const Menubar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Menubar.displayName = MenubarPrimitive.Root.displayName + +const MenubarTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName + +const MenubarSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName + +const MenubarSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName + +const MenubarContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, + ref + ) => ( + + + + ) +) +MenubarContent.displayName = MenubarPrimitive.Content.displayName + +const MenubarItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarItem.displayName = MenubarPrimitive.Item.displayName + +const MenubarCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName + +const MenubarRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName + +const MenubarLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarLabel.displayName = MenubarPrimitive.Label.displayName + +const MenubarSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName + +const MenubarShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +MenubarShortcut.displayname = "MenubarShortcut" + +export { + Menubar, + MenubarMenu, + MenubarTrigger, + MenubarContent, + MenubarItem, + MenubarSeparator, + MenubarLabel, + MenubarCheckboxItem, + MenubarRadioGroup, + MenubarRadioItem, + MenubarPortal, + MenubarSubContent, + MenubarSubTrigger, + MenubarGroup, + MenubarSub, + MenubarShortcut, +} diff --git a/nise-replay-viewer/src/interface/components/ui/sheet.tsx b/nise-replay-viewer/src/interface/components/ui/sheet.tsx new file mode 100644 index 0000000..d64a6fa --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/sheet.tsx @@ -0,0 +1,138 @@ +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Sheet = SheetPrimitive.Root + +const SheetTrigger = SheetPrimitive.Trigger + +const SheetClose = SheetPrimitive.Close + +const SheetPortal = SheetPrimitive.Portal + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} diff --git a/nise-replay-viewer/src/interface/components/ui/slider.tsx b/nise-replay-viewer/src/interface/components/ui/slider.tsx new file mode 100644 index 0000000..e161dae --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/slider.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import * as SliderPrimitive from "@radix-ui/react-slider" + +import { cn } from "@/lib/utils" + +const Slider = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + + +)) +Slider.displayName = SliderPrimitive.Root.displayName + +export { Slider } diff --git a/nise-replay-viewer/src/interface/components/ui/sonner.tsx b/nise-replay-viewer/src/interface/components/ui/sonner.tsx new file mode 100644 index 0000000..1128edf --- /dev/null +++ b/nise-replay-viewer/src/interface/components/ui/sonner.tsx @@ -0,0 +1,29 @@ +import { useTheme } from "next-themes" +import { Toaster as Sonner } from "sonner" + +type ToasterProps = React.ComponentProps + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = "system" } = useTheme() + + return ( + + ) +} + +export { Toaster } diff --git a/nise-replay-viewer/src/interface/composites/Menu.tsx b/nise-replay-viewer/src/interface/composites/Menu.tsx new file mode 100644 index 0000000..ae3a4db --- /dev/null +++ b/nise-replay-viewer/src/interface/composites/Menu.tsx @@ -0,0 +1,82 @@ +import { + Menubar, + MenubarContent, + MenubarItem, + MenubarMenu, + MenubarTrigger, +} from "@/interface/components/ui/menubar"; +import { OsuRenderer } from "@/osu/OsuRenderer"; +import { state } from "@/utils"; + +export function Navbar() { + const { beatmap, mods } = state(); + return ( + +
+
+ +

+ Replay Viewer +

+
+ + + File + + { + state.setState({ aboutDialog: true }); + }} + > + About + + + + + {OsuRenderer.beatmap && ( + <> + {" "} + + Analyzer + + { + state.setState({ dataAnalysisDialog: true }); + }} + > + gRDA + + + + + )} +
+ + {beatmap && ( +
+ {mods?.map((mod) => { + return ( + + ); + })} +
+

Currently Viewing

+ +

+ {beatmap?.metadata.artist} - {beatmap?.metadata.title} +

+
+ +
+ )} +
+ ); +} diff --git a/nise-replay-viewer/src/interface/composites/about-dialog.tsx b/nise-replay-viewer/src/interface/composites/about-dialog.tsx new file mode 100644 index 0000000..7c27f8b --- /dev/null +++ b/nise-replay-viewer/src/interface/composites/about-dialog.tsx @@ -0,0 +1,27 @@ +import { Button } from "@/interface/components/ui/button"; +import { Dialog, DialogContent } from "@/interface/components/ui/dialog"; +import { Badge } from "@/interface/components/ui/badge"; +import { state } from "@/utils"; +export function AboutDialog() { + const { aboutDialog } = state(); + return ( + { + state.setState({ aboutDialog: change }); + }} + > + +
+

+ Replay Viewer +

+
+

Originally developed by Assist Games

+

With modifications to be integrated with nise.moe

+
+
+
+
+ ); +} diff --git a/nise-replay-viewer/src/interface/composites/analysis.-sheet.tsx b/nise-replay-viewer/src/interface/composites/analysis.-sheet.tsx new file mode 100644 index 0000000..8e00bae --- /dev/null +++ b/nise-replay-viewer/src/interface/composites/analysis.-sheet.tsx @@ -0,0 +1,121 @@ +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, +} from "@/interface/components/ui/sheet"; +import { Button } from "@/interface/components/ui/button"; +import { BarChart, XAxis, Bar, ResponsiveContainer } from "recharts"; +import { state } from "@/utils"; +import { gRDA } from "@/osu/Analysis"; +import { OsuRenderer } from "@/osu/OsuRenderer"; + +export function AnalysisSheet() { + const { dataAnalysisDialog, grda } = state(); + + return ( + { + state.setState({ dataAnalysisDialog: opened }); + }} + > + + + gRDA + + Most of the information provided here is forensic and can be used to + detect specific types of cheats, such as Timewarp and Relax. This + process might take up to a minute to do and collect all information. + + + + {grda && ( +
+

+ Response +

+
+

Frametime Averages:

+ + + + + + + +
Normalization rate due to mods {grda.normalizationRate}
+ + + + + + +
+ +
+

Averages:

+
+ Slider Delta Hold Average{" "} + {Math.round( + (grda.sliderDeltaHoldAverage / grda.sliderLength) * 100 + ) / 100} +
+
+ Approximated circle hold delta range ={" "} + {grda.circleExtremes.max - grda.circleExtremes.min} +
+
+ Approximated slider hold delta range ={" "} + {grda.sliderExtremes.max - grda.sliderExtremes.min} +
+
+ +
Circle | Holdtime distribution
+ + + + + + + +
Circle | Press time distribution
+ + Number(a[0]) - Number(b[0]) + )} + > + + + + +
+ )} +
+
+ ); +} diff --git a/nise-replay-viewer/src/interface/composites/helper.tsx b/nise-replay-viewer/src/interface/composites/helper.tsx new file mode 100644 index 0000000..3b886bd --- /dev/null +++ b/nise-replay-viewer/src/interface/composites/helper.tsx @@ -0,0 +1,35 @@ +import middlemouse from "/mouse.svg"; +import scroll from "/scroll.svg"; +import space from "/space.svg"; +import ctrl from "/ctrl.svg"; +import { state } from "@/utils"; + +export function Helper() { + const { replay } = state(); + + if (!replay) return <>; + + return ( +
+
+ Drag Playfield + +
+
+ Seek Beatmap + +
+
+ Pause/Play + +
+ +
+ Zoom + + +
+
+ ); + +} diff --git a/nise-replay-viewer/src/interface/composites/song-slider.tsx b/nise-replay-viewer/src/interface/composites/song-slider.tsx new file mode 100644 index 0000000..f0f9c23 --- /dev/null +++ b/nise-replay-viewer/src/interface/composites/song-slider.tsx @@ -0,0 +1,60 @@ +import { OsuRenderer } from "@/osu/OsuRenderer"; +import { Card } from "../components/ui/card"; +import { Slider } from "../components/ui/slider"; +import { state } from "@/utils"; +import { Button } from "../components/ui/button"; +import { ArrowLeft, ArrowRight, PauseIcon, PlayIcon } from "lucide-react"; + +export function SongSlider() { + const { beatmap, replay, playing, time } = state(); + if (!beatmap || !replay) { + return; + } + return ( + +
+
+

Current time

+

{new Date(time).toISOString().slice(11, 19)}

+ +
+
+ + + +
+
+ +
+ + + { + OsuRenderer.setTime(value[0]); + }} + /> +
+ ); +} diff --git a/nise-replay-viewer/src/lib/utils.ts b/nise-replay-viewer/src/lib/utils.ts new file mode 100644 index 0000000..ec79801 --- /dev/null +++ b/nise-replay-viewer/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/nise-replay-viewer/src/main.tsx b/nise-replay-viewer/src/main.tsx new file mode 100644 index 0000000..e49016f --- /dev/null +++ b/nise-replay-viewer/src/main.tsx @@ -0,0 +1,46 @@ +import p5 from "p5"; +import "./style.css"; +import "./globals.css"; +import { p, setEnv } from "./utils"; +import React from "react"; +import ReactDOM from "react-dom/client"; +import { ThemeProvider } from "./interface/components/theme-provider"; +import { App } from "./interface/App"; +import { Events, hookable } from "./hooks"; +import "@/hooks/starter"; +import { Toaster } from "./interface/components/ui/sonner"; +import { BrowserRouter as Router } from 'react-router-dom'; + +document.addEventListener("contextmenu", (event) => event.preventDefault()); +const handleWheel = hookable(Events.mouseWheel); +window.addEventListener("wheel", handleWheel, { passive: false }); + +async function main() { + new p5(async (p5Instance) => { + setEnv(p5Instance as unknown as p5); + + // Functions that should be expanded into multiple events + p.preload = hookable(Events.preload); + p.setup = hookable(Events.setup); + p.windowResized = hookable(Events.resize); + p.draw = hookable(Events.draw); + p.mousePressed = hookable(Events.mousePressed); + p.mouseDragged = hookable(Events.mouseDragged); + p.mouseReleased = hookable(Events.mouseReleased); + p.keyPressed = hookable(Events.keyPressed); + p.keyReleased = hookable(Events.keyReleased); + }, document.getElementById("app")!); +} + +main(); + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + + + + +); diff --git a/nise-replay-viewer/src/osu/Analysis.ts b/nise-replay-viewer/src/osu/Analysis.ts new file mode 100644 index 0000000..f2169a7 --- /dev/null +++ b/nise-replay-viewer/src/osu/Analysis.ts @@ -0,0 +1,278 @@ +import { modsFromBitmask } from "@osujs/core"; +import { LegacyReplayFrame, Score } from "osu-classes"; +import { + Circle, + StandardBeatmap, + StandardHitObject, +} from "osu-standard-stable"; + +export function getHoldAverages( + formattedPresses: { + start: number; + end: number | null; + duration: number; + object: null; + }[], + map: StandardBeatmap +) { + const circlePresses: { + start: number; + pressStart: number; + holdTime: number; + }[] = []; + const sliderPresses: { + start: number; + pressStart: number; + holdTime: number; + }[] = []; + + let circleHitAverage = 0; + let circleLength = 0; + let circleExtremes = { max: 0, min: 0 }; + + let sliderDeltaHoldAverage = 0; + let sliderLength = 0; + let sliderExtremes = { max: 0, min: 0 }; + + for (const press of formattedPresses) { + let closestObject: StandardHitObject = map.hitObjects[0]; + for (const object of map.hitObjects) { + if ( + Math.abs(press.start - object.startTime) < + Math.abs(press.start - closestObject.startTime) + ) { + closestObject = object; + } + } + press.object = closestObject as any; + if (closestObject instanceof Circle) { + circleHitAverage += press.duration; + circleLength++; + + if (press.duration < 160) { + circlePresses.push({ + start: closestObject.startTime, + pressStart: press.start, + holdTime: press.duration, + }); + if (press.duration > circleExtremes.max) { + circleExtremes.max = press.duration; + } + if (press.duration < circleExtremes.min) { + circleExtremes.min = press.duration; + } + } + } + + if ((closestObject as any).duration) { + if (press.duration && (closestObject as any).duration) { + let sval = press.duration - (closestObject as any).duration; + sliderDeltaHoldAverage += sval; + sliderLength++; + + sliderPresses.push({ + start: closestObject.startTime, + pressStart: press.start, + holdTime: sval, + }); + + if (sval > sliderExtremes.max) { + sliderExtremes.max = sval; + } + if (sval < sliderExtremes.min) { + sliderExtremes.min = sval; + } + } + } + } + + return { + circlePresses, + sliderPresses, + circleLength, + sliderDeltaHoldAverage, + circleHitAverage, + sliderLength, + circleExtremes, + sliderExtremes, + }; +} + +export function getPresses(score: Score) { + let frameTimes: Record = {}; + let moddedFrameTimes: Record = {}; + const mods = modsFromBitmask(score.info.rawMods as number); + let normalizationRate = 1; + if (mods.includes("DOUBLE_TIME") || mods.includes("NIGHT_CORE")) { + normalizationRate = 1.5; + } + if (mods.includes("HALF_TIME")) { + normalizationRate = 0.75; + } + + const presses: { start: number; end: number | null; tempEnd: number }[] = [ + { start: 0, end: 0, tempEnd: 0 }, + ]; + + const presses2: { start: number; end: number | null; tempEnd: number }[] = [ + { start: 0, end: 0, tempEnd: 0 }, + ]; + + score.replay?.frames.forEach((_frame) => { + let frame = _frame as LegacyReplayFrame; + if (!frameTimes[frame.interval]) { + frameTimes[frame.interval] = 1; + } else { + frameTimes[frame.interval] += 1; + } + + if (!moddedFrameTimes[Math.round(frame.interval / normalizationRate)]) { + moddedFrameTimes[Math.round(frame.interval / normalizationRate)] = 1; + } else { + moddedFrameTimes[Math.round(frame.interval / normalizationRate)] += 1; + } + + const lastpress = presses[presses.length - 1]; + if (frame.mouseLeft) { + if (lastpress.end !== null) { + presses.push({ + start: frame.startTime, + end: null, + tempEnd: frame.startTime, + }); + } else { + lastpress.tempEnd = frame.startTime; + } + } else { + if (lastpress.end === null) { + lastpress.end = frame.startTime; + } + } + + const lastpress2 = presses2[presses2.length - 1]; + if (frame.mouseRight) { + if (lastpress2.end !== null) { + presses2.push({ + start: frame.startTime, + end: null, + tempEnd: frame.startTime, + }); + } else { + lastpress2.tempEnd = frame.startTime; + } + } else { + if (lastpress2.end === null) { + lastpress2.end = frame.startTime; + } + } + }); + + return { frameTimes, moddedFrameTimes, presses, presses2, normalizationRate }; +} + +export function getFormattedPresses( + presses: { + start: number; + end: number | null; + tempEnd: number; + }[], + presses2: { + start: number; + end: number | null; + tempEnd: number; + }[], + map: StandardBeatmap +) { + const allPresses = [...presses, ...presses2]; + + const formattedPresses = allPresses.map((press) => ({ + start: press.start, + end: press.end, + duration: press.end! - press.start, + object: null, + })); + + let joined: any = []; + map.hitObjects.forEach((object: any) => { + (joined as any).push({ + start: object.startTime, + end: null, + sduration: (object as any).duration, + }); + }); + + formattedPresses.forEach((object) => { + (joined as any).push({ + start: object.start, + end: object.end, + duration: object.duration, + }); + }); + formattedPresses.sort((a, b) => a.start - b.start); + joined.sort((a: any, b: any) => a.start - b.start); + return { formattedPresses, joined }; +} + +export function baseRound( + x: number, + prec: number = 2, + base: number = 0.05 +): number { + return Number((Math.round(x / base) * base).toFixed(prec)); +} + +export function gRDA(score: Score, map: StandardBeatmap) { + const { frameTimes, moddedFrameTimes, normalizationRate, presses, presses2 } = + getPresses(score); + + const { formattedPresses } = getFormattedPresses(presses, presses2, map); + + const { + circlePresses, + sliderPresses, + circleLength, + sliderDeltaHoldAverage, + circleHitAverage, + sliderLength, + circleExtremes, + sliderExtremes, + } = getHoldAverages(formattedPresses, map); + + const holdCircleDistributionGraph: Record = {}; + + for (const press of circlePresses) { + const normalizedTime = baseRound(press.holdTime, 1, 3); + if (!holdCircleDistributionGraph[normalizedTime]) { + holdCircleDistributionGraph[normalizedTime] = 1; + } else { + holdCircleDistributionGraph[normalizedTime] += 1; + } + } + + const pressCircleDistributionGraph: Record = {}; + + for (const press of circlePresses) { + const normalizedTime = baseRound(press.pressStart - press.start, 1, 3); + if (!pressCircleDistributionGraph[normalizedTime]) { + pressCircleDistributionGraph[normalizedTime] = 1; + } else { + pressCircleDistributionGraph[normalizedTime] += 1; + } + } + + return { + frameTimes, + moddedFrameTimes, + normalizationRate, + holdCircleDistributionGraph, + pressCircleDistributionGraph, + sliderPresses, + circlePresses, + sliderLength, + circleLength, + sliderDeltaHoldAverage, + circleHitAverage, + circleExtremes, + sliderExtremes, + }; +} diff --git a/nise-replay-viewer/src/osu/Drawer.ts b/nise-replay-viewer/src/osu/Drawer.ts new file mode 100644 index 0000000..3b3c7f0 --- /dev/null +++ b/nise-replay-viewer/src/osu/Drawer.ts @@ -0,0 +1,299 @@ +import { Vector2 } from "osu-classes"; +import { Md5 } from "ts-md5"; +import p5 from "p5"; +import { loadImageAsync } from "@/utils"; + +export class Drawer { + private static imageCache: Record = {}; + static images = { + cursor: undefined as any as p5.Image, + cursortrail: undefined as any as p5.Image, + hitcircle: undefined as any as p5.Image, + hitcircleoverlay: undefined as any as p5.Image, + radix: undefined as any as p5.Image, + sliderfollowcircle: undefined as any as p5.Image, + sliderb0: undefined as any as p5.Image, + default0: undefined as any as p5.Image, + default1: undefined as any as p5.Image, + default2: undefined as any as p5.Image, + default3: undefined as any as p5.Image, + default4: undefined as any as p5.Image, + default5: undefined as any as p5.Image, + default6: undefined as any as p5.Image, + default7: undefined as any as p5.Image, + default8: undefined as any as p5.Image, + default9: undefined as any as p5.Image, + }; + private static p: p5; + + static setP(_p: p5) { + this.p = _p; + } + + static async loadDefaultImages() { + for (const imageName of Object.keys(Drawer.images)) { + Drawer.images[imageName as keyof typeof Drawer.images] = + await loadImageAsync(`/${imageName}.png`); + } + } + + static setImages(images: typeof this.images) { + this.images = images; + } + + static setDrawingOpacity(opacity: number) { + //@ts-ignore + this.p.drawingContext.globalAlpha = opacity; + } + + static drawCircleJudgement( + position: Vector2, + radius: number, + judgement: string + ) { + Drawer.p.push(); + Drawer.p.strokeWeight(2); + if (judgement === "OK") { + Drawer.p.stroke(`rgb(106, 176, 76)`); + Drawer.p.fill(`rgb(106, 176, 76)`); + } + if (judgement === "MEH") { + Drawer.p.stroke(`rgb(241, 196, 15)`); + Drawer.p.fill(`rgb(241, 196, 15)`); + } + if (judgement === "MISS") { + Drawer.p.stroke(`rgb(231, 76, 60)`); + Drawer.p.fill(`rgb(231, 76, 60)`); + } + if (judgement !== "GREAT") { + Drawer.p.circle(position.x, position.y, radius * 2); + } + Drawer.p.pop(); + } + + static drawSliderFollowPoint(position: Vector2, radius: number) { + Drawer.p.image( + this.images.sliderfollowcircle, + position.x, + position.y, + radius * 2, + radius * 2 + ); + Drawer.p.push(); + Drawer.p.fill(255, 255, 255, 18); + Drawer.p.circle(position.x, position.y, radius * 4); + Drawer.p.pop(); + } + + static drawHitCircle(position: Vector2, radius: number, comboNumber: number) { + Drawer.p.push(); + Drawer.p.noStroke(); + Drawer.p.fill(160); + Drawer.p.image( + this.images.hitcircle, + position.x, + position.y, + radius * 2, + radius * 2 + ); + Drawer.p.image( + this.images.hitcircleoverlay, + position.x, + position.y, + radius * 2, + radius * 2 + ); + this.drawNumberWithSprites( + comboNumber + 1, + new Vector2(position.x - 0.5, position.y), + radius * 0.4 + ); + + Drawer.p.pop(); + } + + static drawApproachCircle( + position: Vector2, + radius: number, + arScale: number + ) { + if (arScale == 1) return; + Drawer.p.push(); + Drawer.p.noFill(); + Drawer.p.stroke("white"); + Drawer.p.strokeWeight(3); + Drawer.p.circle(position.x, position.y, radius * 2 * arScale); + Drawer.p.pop(); + } + + static drawSliderBody(origin: Vector2, path: Vector2[], radius: number) { + Drawer.p.push(); + + const cacheKey = Md5.hashStr(JSON.stringify(path) + JSON.stringify(radius)); + if (!this.imageCache[cacheKey]) { + const g = Drawer.p.createGraphics(512 * 4, 384 * 4); + g.scale(2); + g.translate(512 - 256, 384 - 192); + //@ts-ignore + const ctx = g.drawingContext; + ctx.lineCap = "round"; + ctx.lineJoin = "round"; + g.translate(origin.x, origin.y); + g.noFill(); + + g.strokeWeight(radius * 2 - 10); + + g.stroke(255); + g.beginShape(); + for (const node of path) { + g.vertex(node.x, node.y); + } + g.endShape(); + + g.strokeWeight(radius * 2 - 17); + g.stroke(10); + g.beginShape(); + for (const node of path) { + g.vertex(node.x, node.y); + } + + g.endShape(); + + for (let i = 0; i < radius * 2 - 17; i += 2) { + g.strokeWeight(radius * 2 - 17 - i); + g.stroke(Math.round((i / (radius * 2 - 17)) * 45)); + g.beginShape(); + for (const node of path) { + g.vertex(node.x, node.y); + } + + g.endShape(); + } + + this.imageCache[cacheKey] = g; + } + Drawer.p.imageMode(Drawer.p.CORNER); + Drawer.p.image(this.imageCache[cacheKey], -256, -192, 512 * 2, 384 * 2); + + Drawer.p.pop(); + } + + static drawCursorPath( + path: { + position: Vector2; + button: { + mouseLeft1: boolean; + mouseLeft2: boolean; + mouseRight1: boolean; + mouseRight2: boolean; + }; + }[], + cursor: { + position: Vector2; + button: { + mouseLeft1: boolean; + mouseLeft2: boolean; + mouseRight1: boolean; + mouseRight2: boolean; + }; + } + ) { + Drawer.p.push(); + //@ts-ignore + const ctx = Drawer.p.drawingContext; + ctx.lineCap = "round"; + ctx.lineJoin = "round"; + Drawer.p.noFill(); + + Drawer.p.push(); + Drawer.p.strokeWeight(2.5); + Drawer.p.stroke("black"); + for (let i = 1; i < path.length; i++) { + const lastFrame = path[i - 1]; + const frame = path[i]; + + Drawer.p.line( + lastFrame.position.x, + lastFrame.position.y, + frame.position.x, + frame.position.y + ); + } + + Drawer.p.pop(); + Drawer.p.strokeWeight(1.5); + Drawer.p.stroke("white"); + + for (let i = 1; i < path.length; i++) { + const lastFrame = path[i - 1]; + const frame = path[i]; + Drawer.p.stroke("white"); + + if (lastFrame.button.mouseLeft1 || lastFrame.button.mouseLeft2) { + Drawer.p.stroke("#BB6BD9"); + } + if (lastFrame.button.mouseRight1 || lastFrame.button.mouseRight2) { + Drawer.p.stroke("#F2994A"); + } + if (lastFrame.button.mouseLeft1 && lastFrame.button.mouseRight1) { + Drawer.p.stroke("red"); + } + Drawer.p.line( + lastFrame.position.x, + lastFrame.position.y, + frame.position.x, + frame.position.y + ); + } + + if (cursor.position) + Drawer.p.image( + this.images.cursor, + cursor.position.x, + cursor.position.y, + 55, + 55 + ); + + Drawer.p.pop(); + } + static drawNumberWithSprites( + number: number, + position: Vector2, + size: number + ) { + Drawer.p.push(); + Drawer.p.imageMode(Drawer.p.CORNER); + const digits = number.toString().split(""); + const digitWidth = size; + const digitHeight = size * 1.2; + const digitSpacing = -size * 0.1; + const totalWidth = digits.length * (digitWidth + digitSpacing); + const x = position.x - totalWidth / 2; + const y = position.y - digitHeight / 2; + digits.forEach((digit, index) => { + const indexer = `default${digit}`; + const image = this.images[indexer as keyof typeof this.images]; + Drawer.p.image( + image, + x + index * (digitWidth + digitSpacing), + y, + digitWidth, + digitHeight + ); + }); + Drawer.p.pop(); + } + static drawField() { + Drawer.p.noFill(); + Drawer.p.stroke(255, 255, 255, 60); + Drawer.p.rect(0, 0, 512, 384, 4); + } + + static beginDrawing() { + Drawer.p.push(); + } + static endDrawing() { + Drawer.p.pop(); + } +} diff --git a/nise-replay-viewer/src/osu/OsuRenderer.ts b/nise-replay-viewer/src/osu/OsuRenderer.ts new file mode 100644 index 0000000..c7704bf --- /dev/null +++ b/nise-replay-viewer/src/osu/OsuRenderer.ts @@ -0,0 +1,413 @@ +import {HitResult, LegacyReplayFrame, Score, Vector2} from "osu-classes"; +import { BeatmapDecoder, BeatmapEncoder } from "osu-parsers"; +import { + Circle, + Slider, + Spinner, + StandardBeatmap, + StandardHardRock, + StandardHitObject, + StandardModCombination, + StandardRuleset, +} from "osu-standard-stable"; +import { Drawer } from "./Drawer"; +import { Vec2 } from "@osujs/math"; +import { clamp, getBeatmap, getReplay } from "@/utils"; +import EventEmitter from "eventemitter3"; + +export enum OsuRendererEvents { + UPDATE = "UPDATE", + LOAD = "LOAD", + PLAY = "PLAY", + TIME = "TIME", +} + +export class OsuRendererBridge extends EventEmitter { + constructor() { + super(); + } +} + +export class OsuRenderer { + private static preempt: number; + private static fadeIn: number; + private static lastRender: number = Date.now(); + + static playing: boolean = false; + + static event = new OsuRendererBridge(); + + static time: number = 0; + static beatmap: StandardBeatmap; + static og_beatmap: StandardBeatmap; + static replay: Score; + static og_replay_mods: StandardModCombination; + static forceHR: boolean | undefined = undefined; + + static loaded: boolean = false; + + static pathWindow: number = 500; + + static purge() { + this.replay = undefined as any; + this.beatmap = undefined as any; + } + + static render() { + if (!this.beatmap || !this.replay) return; + + if (this.time >= this.replay.replay!.length - 500) + this.time = this.replay.replay!.length - 500; + + for (let i = this.beatmap.hitObjects.length - 1; i >= 0; i--) { + this.renderObject(this.beatmap.hitObjects[i]); + } + + if (this.playing) { + this.setTime(this.time + (Date.now() - this.lastRender)); + } + + this.lastRender = Date.now(); + + this.renderPath(); + Drawer.drawField(); + } + + static getOptions() { + return { + replay: this.replay, + mods: this.replay.info.mods?.all, + beatmap: this.og_beatmap, + }; + } + + static getCurrentDifficulty() { + return { + AR: OsuRenderer.og_beatmap?.difficulty?.approachRate, + CS: OsuRenderer.og_beatmap?.difficulty?.circleSize, + OD: OsuRenderer.og_beatmap?.difficulty?.overallDifficulty, + }; + } + + static setMetadata({ AR, CS, OD }: { AR: number; CS: number; OD: number }) { + this.og_beatmap.difficulty.approachRate = AR; + this.og_beatmap.difficulty.circleSize = CS; + this.og_beatmap.difficulty.overallDifficulty = OD; + const tempClone = this.og_beatmap.clone(); + let sendMods = this.replay.info.mods as StandardModCombination; + const hasHardrock = sendMods.all.some((e) => e instanceof StandardHardRock); + + if (this.forceHR !== undefined) { + if (hasHardrock !== this.forceHR) { + this.replay.replay?.frames.forEach((frame) => { + const f = frame as LegacyReplayFrame; + f.position = new Vector2(f.position.x, 384 - f.position.y); + }); + } + if (this.forceHR) { + if (!hasHardrock) { + //@ts-ignore + sendMods._mods.push(new StandardHardRock()); + } + } else { + //@ts-ignore + sendMods._mods = sendMods._mods.filter( + //@ts-ignore + (e) => !(e instanceof StandardHardRock) + ); + } + } + + this.beatmap = this.recreateBeatmap(tempClone, sendMods); + // GameplayAnalyzer.createBucket(this.replay, this.beatmap); + + let fadeIn: number; + let preempt: number; + if (this.beatmap!.difficulty.approachRate <= 5) { + fadeIn = 800 + (400 * (5 - this.beatmap!.difficulty.approachRate)) / 5; + preempt = 1200 + (600 * (5 - this.beatmap!.difficulty.approachRate)) / 5; + } else { + fadeIn = 800 - (500 * (this.beatmap!.difficulty.approachRate - 5)) / 5; + preempt = 1200 - (750 * (this.beatmap!.difficulty.approachRate - 5)) / 5; + } + this.preempt = preempt; + this.fadeIn = fadeIn; + this.event.emit(OsuRendererEvents.UPDATE); + } + + static setPlaying(state: boolean) { + this.playing = state; + this.event.emit(OsuRendererEvents.PLAY); + } + + static refreshMetadata() { + OsuRenderer.setMetadata({ + AR: OsuRenderer.og_beatmap.difficulty.approachRate, + CS: OsuRenderer.og_beatmap.difficulty.circleSize, + OD: OsuRenderer.og_beatmap.difficulty.overallDifficulty, + }); + } + + static async loadReplayFromUrl(url: string) { + OsuRenderer.purge(); + + const response = await fetch(url, { + headers: { + 'X-NISE-REPLAY': '20240303' + } + }); + + const data = await response.json(); + + const { beatmap, replay, mods } = data; + + const i_replay = await getReplay(replay); + i_replay.info.rawMods = mods; + i_replay.info.mods = new StandardModCombination(mods); + + const i_beatmap = await getBeatmap(beatmap, i_replay); + + OsuRenderer.setOptions(i_beatmap, i_replay); + this.event.emit(OsuRendererEvents.LOAD); + } + + static setOptions(beatmap: StandardBeatmap, replay: Score) { + this.forceHR = undefined; + this.replay = replay; + this.beatmap = beatmap; + this.og_beatmap = beatmap.clone(); + this.og_replay_mods = replay.info.mods?.clone() as StandardModCombination; + this.setMetadata({ + AR: this.beatmap.difficulty.approachRate, + CS: this.beatmap.difficulty.circleSize, + OD: this.beatmap.difficulty.overallDifficulty, + }); + } + + static setTime(time: number) { + this.time = clamp(time, 0, OsuRenderer.replay.replay?.length || 0); + this.event.emit(OsuRendererEvents.TIME); + } + + private static renderObject(hitObject: StandardHitObject) { + if (hitObject instanceof Circle) { + this.renderCircle(hitObject); + } + + if (hitObject instanceof Slider) { + this.renderSlider(hitObject); + } + } + + private static calculateEffects(hitObject: StandardHitObject) { + let vEndTime = hitObject.startTime; + + if (hitObject instanceof Slider || hitObject instanceof Spinner) { + vEndTime = hitObject.endTime + 25; + } + + const fadeOut = Math.max( + 0.0, + (this.time - vEndTime) / hitObject.hitWindows.windowFor(HitResult.Meh) + ); + + let opacity = Math.max( + 0.0, + Math.min( + 1.0, + Math.min( + 1.0, + (this.time - hitObject.startTime + this.preempt) / this.fadeIn + ) - fadeOut + ) + ); + + const arScale = Math.max( + 1, + ((hitObject.startTime - this.time) / this.preempt) * 3.0 + 1.0 + ); + + let visible = + this.time > hitObject.startTime - this.preempt && + this.time < vEndTime + hitObject.hitWindows.windowFor(HitResult.Meh); + + if (hitObject instanceof Slider && this.time > hitObject.endTime) { + opacity -= (this.time - hitObject.endTime) / 25; + } + return { + opacity, + arScale, + visible, + }; + } + + private static renderCircle(hitObject: Circle) { + if (hitObject.startTime > this.time + 10000) return; + const { arScale, opacity, visible } = this.calculateEffects(hitObject); + + if (!visible) return; + Drawer.beginDrawing(); + + Drawer.setDrawingOpacity(opacity); + + Drawer.drawApproachCircle( + hitObject.stackedStartPosition, + hitObject.radius, + arScale + ); + Drawer.drawHitCircle( + hitObject.stackedStartPosition, + hitObject.radius, + hitObject.currentComboIndex + ); + + // if (GameplayAnalyzer.renderJudgements[hitObject.startTime]) { + // Drawer.setDrawingOpacity(opacity / 2); + // + // Drawer.drawCircleJudgement( + // hitObject.stackedStartPosition, + // hitObject.radius, + // GameplayAnalyzer.renderJudgements[hitObject.startTime] + // ); + // } + Drawer.endDrawing(); + return arScale; + } + + private static renderSlider(hitObject: Slider) { + if (hitObject.endTime > this.time + 10000) return; + + const { arScale, opacity, visible } = this.calculateEffects(hitObject); + + if (!visible) return; + Drawer.beginDrawing(); + Drawer.setDrawingOpacity(opacity * 0.8); + + Drawer.drawSliderBody( + hitObject.stackedStartPosition, + hitObject.path.path, + hitObject.radius + ); + Drawer.setDrawingOpacity(opacity); + + Drawer.drawApproachCircle( + hitObject.stackedStartPosition, + hitObject.radius, + arScale + ); + Drawer.drawHitCircle( + hitObject.stackedStartPosition, + hitObject.radius, + hitObject.currentComboIndex + ); + + let progress = (this.time - hitObject.startTime) / hitObject.duration; + let position = hitObject.stackedStartPosition.add( + hitObject.path.curvePositionAt(progress, hitObject.repeats + 1) + ); + + if (hitObject.repeats == 0) { + position = hitObject.stackedStartPosition.add( + hitObject.path.positionAt(progress) + ); + } + + let sliderPos = new Vector2(position.x, position.y); + + if (this.time > hitObject.startTime && this.time < hitObject.endTime) { + Drawer.drawSliderFollowPoint(sliderPos, hitObject.radius); + } + + // if (GameplayAnalyzer.renderJudgements[hitObject.startTime]) { + // Drawer.setDrawingOpacity(opacity / 2); + + // Drawer.drawCircleJudgement( + // hitObject.stackedStartPosition, + // hitObject.radius, + // GameplayAnalyzer.renderJudgements[hitObject.startTime] + // ); + // } + Drawer.endDrawing(); + return arScale; + } + + private static renderPath() { + const frames = this.replay.replay!.frames as LegacyReplayFrame[]; + const renderFrames: { + position: Vector2; + button: { + mouseLeft1: boolean; + mouseLeft2: boolean; + mouseRight1: boolean; + mouseRight2: boolean; + }; + }[] = []; + let lastFrame: LegacyReplayFrame | undefined; + let cursorPushed: any = false; + for (const frame of frames) { + if (frame.startTime > this.time) { + if (!cursorPushed) + cursorPushed = { + position: this.interpolateReplayPosition( + lastFrame ? lastFrame : frame, + frame, + this.time + ), + button: { + mouseLeft1: frame.mouseLeft1, + mouseLeft2: frame.mouseLeft2, + mouseRight1: frame.mouseRight1, + mouseRight2: frame.mouseRight2, + }, + }; + if (frame.startTime > this.time + this.pathWindow) break; + } + if (frame.startTime < this.time - this.pathWindow) { + continue; + } + lastFrame = frame; + // renderFrames.push({ + // position: frame.position, + // button: { + // mouseLeft1: frame.mouseLeft1, + // mouseLeft2: frame.mouseLeft2, + // mouseRight1: frame.mouseRight1, + // mouseRight2: frame.mouseRight2, + // }, + // }); + } + + Drawer.drawCursorPath(renderFrames, cursorPushed); + + return cursorPushed; + } + + private static interpolateReplayPosition( + fA: LegacyReplayFrame, + fB: LegacyReplayFrame, + time: number + ): Vector2 { + if (fB.startTime === fA.startTime) { + return fA.position; + } else { + const p = (time - fA.startTime) / (fB.startTime - fA.startTime); + const { x, y } = Vec2.interpolate(fA.position, fB.position, p); + return new Vector2(x, y); + } + } + + private static recreateBeatmap( + beatmap: StandardBeatmap, + mods: StandardModCombination + ) { + const beatmapDecoder = new BeatmapDecoder(); + const beatmapEncoder = new BeatmapEncoder(); + const ruleset = new StandardRuleset(); + + const bp = beatmapDecoder.decodeFromString( + beatmapEncoder.encodeToString(beatmap) + ); + return ruleset.applyToBeatmapWithMods(bp, mods as StandardModCombination); + } + +} diff --git a/nise-replay-viewer/src/renderer.ts b/nise-replay-viewer/src/renderer.ts new file mode 100644 index 0000000..ace565d --- /dev/null +++ b/nise-replay-viewer/src/renderer.ts @@ -0,0 +1,50 @@ +import { Vector } from "p5"; +import { p, state } from "./utils"; +import { OsuRenderer, OsuRendererEvents } from "./osu/OsuRenderer"; +import { Drawer } from "./osu/Drawer"; +import { toast } from "sonner"; + +export class Renderer { + static mouse: Vector; + static OsuRenderer: OsuRenderer = OsuRenderer; + + static async setup() { + Renderer.registerEvents(); + Drawer.setP(p); + + await Drawer.loadDefaultImages(); + } + + static draw() { + if (!OsuRenderer.beatmap) return; + + OsuRenderer.render(); + p.circle(this.mouse.x, this.mouse.y, 25); + } + + static registerEvents() { + // Sync UI with datapath classes + OsuRenderer.event.on(OsuRendererEvents.UPDATE, () => { + const options = OsuRenderer.getOptions(); + state.setState({ + beatmap: options.beatmap, + replay: options.replay, + mods: options.mods, + }); + }); + + OsuRenderer.event.on(OsuRendererEvents.LOAD, () => { + toast(`Successfully loaded replay!`); + }); + OsuRenderer.event.on(OsuRendererEvents.PLAY, () => { + state.setState({ + playing: OsuRenderer.playing, + }); + }); + OsuRenderer.event.on(OsuRendererEvents.TIME, () => { + state.setState({ + time: OsuRenderer.time, + }); + }); + } +} diff --git a/nise-replay-viewer/src/style.css b/nise-replay-viewer/src/style.css new file mode 100644 index 0000000..6b458e3 --- /dev/null +++ b/nise-replay-viewer/src/style.css @@ -0,0 +1,35 @@ +#app { + margin:0; + padding: 0; + position: absolute; + z-index: -1; +} + +#root{ +} + +body{ + margin:0; + overflow: hidden; +} + + /* width */ + ::-webkit-scrollbar { + width: 6px; + } + + /* Track */ + ::-webkit-scrollbar-track { + background: #f1f1f10a; + } + + /* Handle */ + ::-webkit-scrollbar-thumb { + background: #88888852; + border-radius: 5px; + } + + /* Handle on hover */ + ::-webkit-scrollbar-thumb:hover { + background: #555; + } \ No newline at end of file diff --git a/nise-replay-viewer/src/tooling/brush.ts b/nise-replay-viewer/src/tooling/brush.ts new file mode 100644 index 0000000..79ccce3 --- /dev/null +++ b/nise-replay-viewer/src/tooling/brush.ts @@ -0,0 +1,15 @@ +import { Tool } from "./tool"; +import { Singleton } from "@/decorators/singleton"; + +@Singleton +export class BrushTool extends Tool { + mousePressed(): void { + throw new Error("Method not implemented."); + } + mouseReleased(): void { + throw new Error("Method not implemented."); + } + tick(): void { + throw new Error("Method not implemented."); + } +} diff --git a/nise-replay-viewer/src/tooling/tool.ts b/nise-replay-viewer/src/tooling/tool.ts new file mode 100644 index 0000000..771d44e --- /dev/null +++ b/nise-replay-viewer/src/tooling/tool.ts @@ -0,0 +1,5 @@ +export abstract class Tool { + abstract mousePressed(): void; + abstract mouseReleased(): void; + abstract tick(): void; +} diff --git a/nise-replay-viewer/src/utils.ts b/nise-replay-viewer/src/utils.ts new file mode 100644 index 0000000..bb6958b --- /dev/null +++ b/nise-replay-viewer/src/utils.ts @@ -0,0 +1,84 @@ +import { BeatmapDecoder } from "osu-parsers"; +import { ScoreDecoder } from "../osu-parsers"; +import { StandardRuleset, StandardBeatmap } from "osu-standard-stable"; + +import { IMod, Score } from "osu-classes"; +import p5, { Image } from "p5"; +import { create } from "zustand"; + +const ruleset = new StandardRuleset(); +const scoreDecoder = new ScoreDecoder(); +const beatmapDecoder = new BeatmapDecoder(); + +export async function getReplay(buffer: ArrayBuffer) { + const repl = await scoreDecoder.decodeFromBuffer(buffer); + repl.info.ruleset = ruleset; + return repl; +} + +export async function getBeatmap(mapText: string, scoreBase: Score) { + const map = beatmapDecoder.decodeFromString(mapText); + + const score = scoreBase.info; + score.accuracy = + (score.count300 + score.count100 / 3 + score.count50 / 6) / + (score.count300 + score.count100 + score.count50 + score.countMiss); + + return ruleset.applyToBeatmap(map); +} + +export async function loadImageAsync(image: string): Promise { + return new Promise((res) => { + p.loadImage(image, (img) => { + res(img); + }); + }); +} + +export function clamp(num: number, min: number, max: number) { + if (num > max) return max; + if (num < min) return min; + return num +} + +export const state = create<{ + beatmap: StandardBeatmap | null; + replay: Score | null; + metadataEditorDialog: boolean; + openDialog: boolean; + saveDialog: boolean; + dataAnalysisDialog: boolean; + achivementsDialog: boolean; + aboutDialog: boolean; + grda: any; + tool: "cursor" | "brush" | "advanced" | "smoother"; + mods: IMod[] | null; + playing: boolean; + time: number; +}>(() => ({ + metadataEditorDialog: false, + openDialog: false, + saveDialog: false, + aboutDialog: false, + achivementsDialog: false, + dataAnalysisDialog: false, + beatmap: null, + replay: null, + grda: null, + tool: "cursor", + mods: null, + playing: false, + time: 0, +})); + +state.subscribe((newState) => { + if (newState.beatmap) { + document.title = `${newState.beatmap.metadata.artist} - ${newState.beatmap.metadata.titleUnicode} | Replay Inspector` + } +}) + +export let p: p5; + +export function setEnv(_p: p5) { + p = _p; +} diff --git a/nise-replay-viewer/tailwind.config.js b/nise-replay-viewer/tailwind.config.js new file mode 100644 index 0000000..0377ea1 --- /dev/null +++ b/nise-replay-viewer/tailwind.config.js @@ -0,0 +1,76 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class"], + content: [ + './pages/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + './app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', + ], + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: 0 }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: 0 }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} \ No newline at end of file diff --git a/nise-replay-viewer/tsconfig.json b/nise-replay-viewer/tsconfig.json new file mode 100644 index 0000000..f50d4b4 --- /dev/null +++ b/nise-replay-viewer/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "types": ["vite/client"], + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "jsx": "react-jsx", + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["./src", "node_modules/@types/p5/index.d.ts"] +} diff --git a/nise-replay-viewer/vite.config.ts b/nise-replay-viewer/vite.config.ts new file mode 100644 index 0000000..cf45b55 --- /dev/null +++ b/nise-replay-viewer/vite.config.ts @@ -0,0 +1,12 @@ +import path from "path"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, +}); diff --git a/nise-replay-viewer/yarn.lock b/nise-replay-viewer/yarn.lock new file mode 100644 index 0000000..f2aa269 --- /dev/null +++ b/nise-replay-viewer/yarn.lock @@ -0,0 +1,2011 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.9": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz" + integrity sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ== + +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz" + integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.3" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.2" + "@babel/parser" "^7.23.3" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.3" + "@babel/types" "^7.23.3" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz" + integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg== + dependencies: + "@babel/types" "^7.23.3" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.15" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-plugin-utils@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== + +"@babel/helpers@^7.23.2": + version "7.23.2" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz" + integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.2" + "@babel/types" "^7.23.0" + +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz" + integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw== + +"@babel/plugin-transform-react-jsx-self@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz" + integrity sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-jsx-source@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz" + integrity sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.5": + version "7.23.8" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz" + integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.23.2", "@babel/traverse@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz" + integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.3" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.3" + "@babel/types" "^7.23.3" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz" + integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@esbuild/linux-x64@0.18.20": + version "0.18.20" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz" + integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== + +"@floating-ui/core@^1.5.3": + version "1.5.3" + resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.3.tgz" + integrity sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q== + dependencies: + "@floating-ui/utils" "^0.2.0" + +"@floating-ui/dom@^1.5.4": + version "1.5.4" + resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.4.tgz" + integrity sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ== + dependencies: + "@floating-ui/core" "^1.5.3" + "@floating-ui/utils" "^0.2.0" + +"@floating-ui/react-dom@^2.0.0": + version "2.0.6" + resolved "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.6.tgz" + integrity sha512-IB8aCRFxr8nFkdYZgH+Otd9EVQPJoynxeFRGTB8voPoZMRWo8XjYuCRgpI1btvuKY69XMiLnW+ym7zoBHM90Rw== + dependencies: + "@floating-ui/dom" "^1.5.4" + +"@floating-ui/utils@^0.2.0": + version "0.2.1" + resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz" + integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@next/env@14.1.0": + version "14.1.0" + resolved "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz" + integrity sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw== + +"@next/swc-linux-x64-gnu@14.1.0": + version "14.1.0" + resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz" + integrity sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ== + +"@next/swc-linux-x64-musl@14.1.0": + version "14.1.0" + resolved "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz" + integrity sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@osujs/core@^0.0.6": + version "0.0.6" + resolved "https://registry.npmjs.org/@osujs/core/-/core-0.0.6.tgz" + integrity sha512-hnxdcjcMylS95NMtoAYwZZxFMsDkZDKAJk2nK6OCHwPXLaaW4DoVBdBFEoWy1VuuR9yAP8b1yATZtsLeaCH2mA== + +"@osujs/math@^0.0.4", "@osujs/math@0.0.4": + version "0.0.4" + resolved "https://registry.npmjs.org/@osujs/math/-/math-0.0.4.tgz" + integrity sha512-4zIvKjsS2ONVQIifRoJLT0BH3YldkOW88AG5yOZ8thjRG9Btdd1l9syAQWSDUchp7t3S9Ducquhctyv2v5lwiA== + +"@radix-ui/number@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz" + integrity sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/primitive@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz" + integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-arrow@1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz" + integrity sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.3" + +"@radix-ui/react-collection@1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz" + integrity sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-slot" "1.0.2" + +"@radix-ui/react-compose-refs@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz" + integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-context-menu@^2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.1.5.tgz" + integrity sha512-R5XaDj06Xul1KGb+WP8qiOh7tKJNz2durpLBXAGZjSVtctcRFCuEvy2gtMwRJGePwQQE5nV77gs4FwRi8T+r2g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-menu" "2.0.6" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-controllable-state" "1.0.1" + +"@radix-ui/react-context@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz" + integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-dialog@^1.0.5": + version "1.0.5" + resolved "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz" + integrity sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-dismissable-layer" "1.0.5" + "@radix-ui/react-focus-guards" "1.0.1" + "@radix-ui/react-focus-scope" "1.0.4" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-portal" "1.0.4" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-slot" "1.0.2" + "@radix-ui/react-use-controllable-state" "1.0.1" + aria-hidden "^1.1.1" + react-remove-scroll "2.5.5" + +"@radix-ui/react-direction@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz" + integrity sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-dismissable-layer@1.0.5": + version "1.0.5" + resolved "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz" + integrity sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-escape-keydown" "1.0.3" + +"@radix-ui/react-focus-guards@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz" + integrity sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-focus-scope@1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz" + integrity sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + +"@radix-ui/react-id@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz" + integrity sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-layout-effect" "1.0.1" + +"@radix-ui/react-label@^2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz" + integrity sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.3" + +"@radix-ui/react-menu@2.0.6": + version "2.0.6" + resolved "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz" + integrity sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-collection" "1.0.3" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-direction" "1.0.1" + "@radix-ui/react-dismissable-layer" "1.0.5" + "@radix-ui/react-focus-guards" "1.0.1" + "@radix-ui/react-focus-scope" "1.0.4" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-popper" "1.1.3" + "@radix-ui/react-portal" "1.0.4" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-roving-focus" "1.0.4" + "@radix-ui/react-slot" "1.0.2" + "@radix-ui/react-use-callback-ref" "1.0.1" + aria-hidden "^1.1.1" + react-remove-scroll "2.5.5" + +"@radix-ui/react-menubar@^1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.0.4.tgz" + integrity sha512-bHgUo9gayKZfaQcWSSLr++LyS0rgh+MvD89DE4fJ6TkGHvjHgPaBZf44hdka7ogOxIOdj9163J+5xL2Dn4qzzg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-collection" "1.0.3" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-direction" "1.0.1" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-menu" "2.0.6" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-roving-focus" "1.0.4" + "@radix-ui/react-use-controllable-state" "1.0.1" + +"@radix-ui/react-popper@1.1.3": + version "1.1.3" + resolved "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz" + integrity sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w== + dependencies: + "@babel/runtime" "^7.13.10" + "@floating-ui/react-dom" "^2.0.0" + "@radix-ui/react-arrow" "1.0.3" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@radix-ui/react-use-rect" "1.0.1" + "@radix-ui/react-use-size" "1.0.1" + "@radix-ui/rect" "1.0.1" + +"@radix-ui/react-portal@1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz" + integrity sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.3" + +"@radix-ui/react-presence@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz" + integrity sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + +"@radix-ui/react-primitive@1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz" + integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-slot" "1.0.2" + +"@radix-ui/react-roving-focus@1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz" + integrity sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-collection" "1.0.3" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-direction" "1.0.1" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-controllable-state" "1.0.1" + +"@radix-ui/react-slider@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.1.2.tgz" + integrity sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/number" "1.0.1" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-collection" "1.0.3" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-direction" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-controllable-state" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@radix-ui/react-use-previous" "1.0.1" + "@radix-ui/react-use-size" "1.0.1" + +"@radix-ui/react-slot@^1.0.2", "@radix-ui/react-slot@1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz" + integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + +"@radix-ui/react-use-callback-ref@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz" + integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-use-controllable-state@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz" + integrity sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.1" + +"@radix-ui/react-use-escape-keydown@1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz" + integrity sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.1" + +"@radix-ui/react-use-layout-effect@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz" + integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-use-previous@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz" + integrity sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-use-rect@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz" + integrity sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/rect" "1.0.1" + +"@radix-ui/react-use-size@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz" + integrity sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-layout-effect" "1.0.1" + +"@radix-ui/rect@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz" + integrity sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ== + dependencies: + "@babel/runtime" "^7.13.10" + +"@remix-run/router@1.15.2": + version "1.15.2" + resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz" + integrity sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q== + +"@swc/helpers@0.5.2": + version "0.5.2" + resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz" + integrity sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw== + dependencies: + tslib "^2.4.0" + +"@types/babel__core@^7.20.4": + version "7.20.4" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz" + integrity sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.7" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz" + integrity sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.20.4" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz" + integrity sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA== + dependencies: + "@babel/types" "^7.20.7" + +"@types/d3-array@^3.0.3": + version "3.2.1" + resolved "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz" + integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg== + +"@types/d3-color@*": + version "3.1.3" + resolved "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz" + integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== + +"@types/d3-ease@^3.0.0": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz" + integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== + +"@types/d3-interpolate@^3.0.1": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz" + integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== + dependencies: + "@types/d3-color" "*" + +"@types/d3-path@*": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz" + integrity sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA== + +"@types/d3-scale@^4.0.2": + version "4.0.8" + resolved "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz" + integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ== + dependencies: + "@types/d3-time" "*" + +"@types/d3-shape@^3.1.0": + version "3.1.6" + resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz" + integrity sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA== + dependencies: + "@types/d3-path" "*" + +"@types/d3-time@*", "@types/d3-time@^3.0.0": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz" + integrity sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw== + +"@types/d3-timer@^3.0.0": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz" + integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== + +"@types/events@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz" + integrity sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g== + +"@types/node@>= 14": + version "20.11.5" + resolved "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz" + integrity sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w== + dependencies: + undici-types "~5.26.4" + +"@types/p5@^0.9.1": + version "0.9.1" + resolved "https://registry.npmjs.org/@types/p5/-/p5-0.9.1.tgz" + integrity sha512-4glOKdqdBiRWDFZwi/MjHudPV2U4t2L4fTTSacGapfFxyNXzZcAshAjqmrJkCIZcFlhRBEAL7AM95xRDMfrIwg== + +"@types/prop-types@*": + version "15.7.10" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz" + integrity sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A== + +"@types/react-dom@*", "@types/react-dom@^18.2.15": + version "18.2.15" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.15.tgz" + integrity sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^16.8.0 || ^17.0.0 || ^18.0.0", "@types/react@^16.9.0 || ^17.0.0 || ^18.0.0", "@types/react@^18.2.37", "@types/react@>=16.8": + version "18.2.37" + resolved "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz" + integrity sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.6" + resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz" + integrity sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA== + +"@vitejs/plugin-react@^4.2.0": + version "4.2.0" + resolved "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.0.tgz" + integrity sha512-+MHTH/e6H12kRp5HUkzOGqPMksezRMmW+TNzlh/QXfI8rRf6l2Z2yH/v12no1UvTwhZgEDMuQ7g7rrfMseU6FQ== + dependencies: + "@babel/core" "^7.23.3" + "@babel/plugin-transform-react-jsx-self" "^7.23.3" + "@babel/plugin-transform-react-jsx-source" "^7.23.3" + "@types/babel__core" "^7.20.4" + react-refresh "^0.14.0" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +aria-hidden@^1.1.1: + version "1.2.3" + resolved "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz" + integrity sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ== + dependencies: + tslib "^2.0.0" + +attr-accept@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz" + integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== + +autoprefixer@^10.4.16: + version "10.4.16" + resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz" + integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== + dependencies: + browserslist "^4.21.10" + caniuse-lite "^1.0.30001538" + fraction.js "^4.3.6" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.21.10, browserslist@^4.21.9, "browserslist@>= 4.21.0": + version "4.22.1" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz" + integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== + dependencies: + caniuse-lite "^1.0.30001541" + electron-to-chromium "^1.4.535" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" + +busboy@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541, caniuse-lite@^1.0.30001579: + version "1.0.30001579" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz" + integrity sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +class-variance-authority@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz" + integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A== + dependencies: + clsx "2.0.0" + +client-only@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + +clsx@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz" + integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== + +clsx@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz" + integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csstype@^3.0.2: + version "3.1.2" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + +d3-array@^3.1.6, "d3-array@2 - 3", "d3-array@2.10.0 - 3": + version "3.2.4" + resolved "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +"d3-color@1 - 3": + version "3.1.0" + resolved "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-ease@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +"d3-format@1 - 3": + version "3.1.0" + resolved "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +d3-interpolate@^3.0.1, "d3-interpolate@1.2.0 - 3": + version "3.0.1" + resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +d3-path@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-scale@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +d3-shape@^3.1.0: + version "3.2.0" + resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4": + version "4.1.0" + resolved "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +d3-time@^3.0.0, "d3-time@1 - 3", "d3-time@2.1.1 - 3": + version "3.1.0" + resolved "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +d3-timer@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + +debug@^4.1.0: + 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" + +decimal.js-light@^2.4.1: + version "2.5.1" + resolved "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + +detect-node-es@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz" + integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" + +electron-to-chromium@^1.4.535: + version "1.4.588" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.588.tgz" + integrity sha512-soytjxwbgcCu7nh5Pf4S2/4wa6UIu+A3p03U2yVr53qGxi1/VTR3ENI+p50v+UxqqZAfl48j3z55ud7VHIOr9w== + +esbuild@^0.18.10: + version "0.18.20" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz" + integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== + optionalDependencies: + "@esbuild/android-arm" "0.18.20" + "@esbuild/android-arm64" "0.18.20" + "@esbuild/android-x64" "0.18.20" + "@esbuild/darwin-arm64" "0.18.20" + "@esbuild/darwin-x64" "0.18.20" + "@esbuild/freebsd-arm64" "0.18.20" + "@esbuild/freebsd-x64" "0.18.20" + "@esbuild/linux-arm" "0.18.20" + "@esbuild/linux-arm64" "0.18.20" + "@esbuild/linux-ia32" "0.18.20" + "@esbuild/linux-loong64" "0.18.20" + "@esbuild/linux-mips64el" "0.18.20" + "@esbuild/linux-ppc64" "0.18.20" + "@esbuild/linux-riscv64" "0.18.20" + "@esbuild/linux-s390x" "0.18.20" + "@esbuild/linux-x64" "0.18.20" + "@esbuild/netbsd-x64" "0.18.20" + "@esbuild/openbsd-x64" "0.18.20" + "@esbuild/sunos-x64" "0.18.20" + "@esbuild/win32-arm64" "0.18.20" + "@esbuild/win32-ia32" "0.18.20" + "@esbuild/win32-x64" "0.18.20" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +eventemitter3@^4.0.1: + version "4.0.7" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +fast-equals@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz" + integrity sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ== + +fast-glob@^3.3.0: + version "3.3.2" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +file-selector@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz" + integrity sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw== + dependencies: + tslib "^2.4.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +fraction.js@^4.3.6: + version "4.3.7" + resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-nonce@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz" + integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.6: + version "7.1.6" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +graceful-fs@^4.2.11: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +jiti@^1.19.1: + version "1.21.0" + resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz" + integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +lilconfig@^2.0.5, lilconfig@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +lodash@^4.17.19: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lucide-react@^0.292.0: + version "0.292.0" + resolved "https://registry.npmjs.org/lucide-react/-/lucide-react-0.292.0.tgz" + integrity sha512-rRgUkpEHWpa5VCT66YscInCQmQuPCB1RFRzkkxMxg4b+jaL0V12E3riWWR2Sh5OIiUhCwGW/ZExuEO4Az32E6Q== + +lzma-js-simple-v2@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/lzma-js-simple-v2/-/lzma-js-simple-v2-1.2.2.tgz" + integrity sha512-dHjXHlgcS8r2K+4t8kAycGE4xMVQxPzMTCrrYhaFEtQDmjXh/A7xq4D88oOfEtjTR9XFzTAML9AuBrm4jXLigA== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.6: + version "3.3.7" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +next-themes@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz" + integrity sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A== + +next@*: + version "14.1.0" + resolved "https://registry.npmjs.org/next/-/next-14.1.0.tgz" + integrity sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q== + dependencies: + "@next/env" "14.1.0" + "@swc/helpers" "0.5.2" + busboy "1.6.0" + caniuse-lite "^1.0.30001579" + graceful-fs "^4.2.11" + postcss "8.4.31" + styled-jsx "5.1.1" + optionalDependencies: + "@next/swc-darwin-arm64" "14.1.0" + "@next/swc-darwin-x64" "14.1.0" + "@next/swc-linux-arm64-gnu" "14.1.0" + "@next/swc-linux-arm64-musl" "14.1.0" + "@next/swc-linux-x64-gnu" "14.1.0" + "@next/swc-linux-x64-musl" "14.1.0" + "@next/swc-win32-arm64-msvc" "14.1.0" + "@next/swc-win32-ia32-msvc" "14.1.0" + "@next/swc-win32-x64-msvc" "14.1.0" + +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +osu-classes@^3.0.0, osu-classes@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/osu-classes/-/osu-classes-3.0.1.tgz" + integrity sha512-EI0pMoyUAgrvGHeOywaglHaGpjxhCHtwxPsK7kKJMDGmSgT8lvD3Ebs7vkSNPcKAuBX0VMbfsnSrwE9ArkOXyw== + +osu-parsers@^4.1.6: + version "4.1.6" + resolved "https://registry.npmjs.org/osu-parsers/-/osu-parsers-4.1.6.tgz" + integrity sha512-k0PQaWBEZEMiweminI/A4j0lLTvsbHvhU/McMI94yJy+ME1ZfggAOp3vVee1P0Ze0Nu+pT/Ir7b0AlrhJO9IsQ== + dependencies: + lzma-js-simple-v2 "^1.2.2" + +osu-standard-stable@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/osu-standard-stable/-/osu-standard-stable-5.0.0.tgz" + integrity sha512-qCzS1DFStHjgeDZguLRY5k4OgJSNXHG+sUdi1jm2Xwgk23QsyexwRQyTs5phFMuAnQmxeyjzWELLHOo99A/gMA== + +p5@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/p5/-/p5-1.3.1.tgz" + integrity sha512-g7W2htgEwiAEGcl0WHccAJKbunUJwrUojUSR9+KihphJ33p5VpDdh1K8pDx4ppYjOr/lVEXaZ1XXDj27nwlNOg== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz" + integrity sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA== + dependencies: + lilconfig "^2.0.5" + yaml "^2.1.1" + +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz" + integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== + dependencies: + postcss-selector-parser "^6.0.11" + +postcss-selector-parser@^6.0.11: + version "6.0.13" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.0.0, postcss@^8.1.0, postcss@^8.2.14, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.27, postcss@^8.4.31, postcss@>=8.0.9, postcss@8.4.31: + version "8.4.31" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +react-dom@*, "react-dom@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8 || ^17.0 || ^18.0", react-dom@^18.0.0, react-dom@^18.2.0, react-dom@>=15.0.0, react-dom@>=16.8, react-dom@>=16.8.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-dropzone@^14.2.3: + version "14.2.3" + resolved "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz" + integrity sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug== + dependencies: + attr-accept "^2.2.2" + file-selector "^0.6.0" + prop-types "^15.8.1" + +react-is@^16.10.2, react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-refresh@^0.14.0: + version "0.14.0" + resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz" + integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== + +react-remove-scroll-bar@^2.3.3: + version "2.3.4" + resolved "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz" + integrity sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A== + dependencies: + react-style-singleton "^2.2.1" + tslib "^2.0.0" + +react-remove-scroll@2.5.5: + version "2.5.5" + resolved "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz" + integrity sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw== + dependencies: + react-remove-scroll-bar "^2.3.3" + react-style-singleton "^2.2.1" + tslib "^2.1.0" + use-callback-ref "^1.3.0" + use-sidecar "^1.1.2" + +react-router-dom@^6.22.2: + version "6.22.2" + resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz" + integrity sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ== + dependencies: + "@remix-run/router" "1.15.2" + react-router "6.22.2" + +react-router@6.22.2: + version "6.22.2" + resolved "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz" + integrity sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw== + dependencies: + "@remix-run/router" "1.15.2" + +react-smooth@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.5.tgz" + integrity sha512-BMP2Ad42tD60h0JW6BFaib+RJuV5dsXJK9Baxiv/HlNFjvRLqA9xrNKxVWnUIZPQfzUwGXIlU/dSYLU+54YGQA== + dependencies: + fast-equals "^5.0.0" + react-transition-group "2.9.0" + +react-style-singleton@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz" + integrity sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g== + dependencies: + get-nonce "^1.0.0" + invariant "^2.2.4" + tslib "^2.0.0" + +react-transition-group@2.9.0: + version "2.9.0" + resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz" + integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== + dependencies: + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-lifecycles-compat "^3.0.4" + +react@*, "react@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.5.1 || ^17.0.0 || ^18.0.0", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", react@^18.0.0, react@^18.2.0, "react@>= 16.8 || 18.0.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", react@>=15.0.0, react@>=16.8, react@>=16.8.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +recharts-scale@^0.4.4: + version "0.4.5" + resolved "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz" + integrity sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w== + dependencies: + decimal.js-light "^2.4.1" + +recharts@^2.10.4: + version "2.10.4" + resolved "https://registry.npmjs.org/recharts/-/recharts-2.10.4.tgz" + integrity sha512-/Q7/wdf8bW91lN3NEeCjL9RWfaiXQViJFgdnas4Eix/I8B9HAI3tHHK/CW/zDfgRMh4fzW1zlfjoz1IAapLO1Q== + dependencies: + clsx "^2.0.0" + eventemitter3 "^4.0.1" + lodash "^4.17.19" + react-is "^16.10.2" + react-smooth "^2.0.5" + recharts-scale "^0.4.4" + tiny-invariant "^1.3.1" + victory-vendor "^36.6.8" + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +resolve@^1.1.7, resolve@^1.22.2: + version "1.22.8" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rollup@^3.27.1: + version "3.29.4" + resolved "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz" + integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== + optionalDependencies: + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +sonner@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/sonner/-/sonner-1.3.1.tgz" + integrity sha512-+rOAO56b2eI3q5BtgljERSn2umRk63KFIvgb2ohbZ5X+Eb5u+a/7/0ZgswYqgBMg8dyl7n6OXd9KasA8QF9ToA== + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +styled-jsx@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz" + integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== + dependencies: + client-only "0.0.1" + +sucrase@^3.32.0: + version "3.34.0" + resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz" + integrity sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "7.1.6" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tailwind-merge@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.0.tgz" + integrity sha512-SqqhhaL0T06SW59+JVNfAqKdqLs0497esifRrZ7jOaefP3o64fdFNDMrAQWZFMxTLJPiHVjRLUywT8uFz1xNWQ== + dependencies: + "@babel/runtime" "^7.23.5" + +tailwindcss-animate@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz" + integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== + +tailwindcss@^3.3.5, "tailwindcss@>=3.0.0 || insiders": + version "3.3.5" + resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz" + integrity sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.5.3" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.0" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.19.1" + lilconfig "^2.1.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" + postcss-selector-parser "^6.0.11" + resolve "^1.22.2" + sucrase "^3.32.0" + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +tiny-invariant@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +ts-md5@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz" + integrity sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg== + +tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0: + version "2.6.2" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +typescript@^4.2.3: + version "4.2.4" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz" + integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +use-callback-ref@^1.3.0: + version "1.3.1" + resolved "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.1.tgz" + integrity sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ== + dependencies: + tslib "^2.0.0" + +use-sidecar@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz" + integrity sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw== + dependencies: + detect-node-es "^1.1.0" + tslib "^2.0.0" + +use-sync-external-store@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utility-types@^3.10.0: + version "3.11.0" + resolved "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz" + integrity sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw== + +victory-vendor@^36.6.8: + version "36.8.2" + resolved "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.8.2.tgz" + integrity sha512-NfSQi7ISCdBbDpn3b6rg+8RpFZmWIM9mcks48BbogHE2F6h1XKdA34oiCKP5hP1OGvTotDRzsexiJKzrK4Exuw== + dependencies: + "@types/d3-array" "^3.0.3" + "@types/d3-ease" "^3.0.0" + "@types/d3-interpolate" "^3.0.1" + "@types/d3-scale" "^4.0.2" + "@types/d3-shape" "^3.1.0" + "@types/d3-time" "^3.0.0" + "@types/d3-timer" "^3.0.0" + d3-array "^3.1.6" + d3-ease "^3.0.1" + d3-interpolate "^3.0.1" + d3-scale "^4.0.2" + d3-shape "^3.1.0" + d3-time "^3.0.0" + d3-timer "^3.0.1" + +"vite@^4.2.0 || ^5.0.0", vite@^4.4.5: + version "4.5.2" + resolved "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz" + integrity sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w== + dependencies: + esbuild "^0.18.10" + postcss "^8.4.27" + rollup "^3.27.1" + optionalDependencies: + fsevents "~2.3.2" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yaml@^2.1.1: + version "2.3.4" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz" + integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== + +zustand@^4.4.1: + version "4.4.1" + resolved "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz" + integrity sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw== + dependencies: + use-sync-external-store "1.2.0"