From 15900b968fa76a8d3b3f70ae25df3696681cba7f Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Tue, 21 May 2024 19:07:52 +0100 Subject: [PATCH] feat: switch to biome to ensure consistency & apply various fixes (#759) --- .vscode/settings.json | 9 + biome.json | 41 + package.json | 12 +- pnpm-lock.yaml | 2249 +------- resources/blacklist.json | 2 +- resources/snippets.json | 48 +- src/app.tsx | 54 +- src/components/Button.tsx | 2 +- src/components/Card/AuthorsDiv.tsx | 6 +- src/components/Card/Card.tsx | 249 +- src/components/Card/TagsDiv.tsx | 51 +- src/components/Grid.tsx | 587 +- src/components/Icons/DownloadIcon.tsx | 5 +- src/components/Icons/GitHubIcon.tsx | 7 +- src/components/Icons/LoadMoreIcon.tsx | 26 +- src/components/Icons/LoadingIcon.tsx | 46 +- src/components/Icons/SettingsIcon.tsx | 5 +- .../Icons/ThemeDeveloperToolsIcon.tsx | 17 +- src/components/Icons/TooltipIcon.tsx | 5 +- src/components/Icons/TrashIcon.tsx | 5 +- src/components/Modals/BackupModal/index.tsx | 25 +- src/components/Modals/Reload/index.tsx | 20 +- src/components/Modals/Settings/ConfigRow.tsx | 32 +- src/components/Modals/Settings/TabRow.tsx | 40 +- src/components/Modals/Settings/index.tsx | 59 +- src/components/Modals/Snippet/index.tsx | 154 +- src/components/Modals/ThemeDevTools/index.tsx | 36 +- src/components/Modals/Update/index.tsx | 38 +- src/components/ReadmePage.tsx | 115 +- src/components/Sortbox.tsx | 16 +- src/components/TabBar.tsx | 191 +- src/components/Toggle.tsx | 9 +- src/constants.ts | 15 +- src/extensions/extension.tsx | 57 +- src/logic/FetchRemotes.ts | 125 +- src/logic/LaunchModals.tsx | 125 +- src/logic/Utils.ts | 277 +- src/resources/locales/index.ts | 16 +- src/resources/locales/uk.json | 6 +- src/types/marketplace-types.d.ts | 37 +- src/types/spicetify.d.ts | 4736 ++++++++--------- tsconfig.json | 30 +- 42 files changed, 3800 insertions(+), 5785 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 biome.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..54b95026 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "always", + "quickfix.biome": "always", + "source.organizeImports": "never", + "source.fixAll": "always" + }, + "editor.defaultFormatter": "biomejs.biome" +} \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..1fb32962 --- /dev/null +++ b/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off" + }, + "a11y": { + "useKeyWithClickEvents": "off", + "useButtonType": "off" + }, + "security": { + "noDangerouslySetInnerHtml": "off" + } + } + }, + "formatter": { + "enabled": true, + "formatWithErrors": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 150 + }, + "javascript": { + "formatter": { + "trailingComma": "none", + "arrowParentheses": "always" + } + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true, + "defaultBranch": "main" + } +} diff --git a/package.json b/package.json index 0475b4af..8f1d24e4 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "build:local": "spicetify-creator --out=dist --minify", "build:prod": "pnpm build:local && pnpm copy:docs", "copy:docs": "copyfiles README.md dist/", - "lint": "eslint --fix src", - "lint:ci": "eslint src", + "lint": "biome check --apply *", + "lint:ci": "biome check *", "type-check": "tsc --noEmit", "watch": "spicetify-creator --watch", "prepare": "husky install", @@ -30,17 +30,13 @@ "@types/react": "18.2.0", "@types/react-dom": "18.2.0", "@types/semver": "^7.5.8", - "@typescript-eslint/eslint-plugin": "^7.8.0", - "@typescript-eslint/parser": "^7.8.0", "copyfiles": "^2.4.1", - "eslint": "^8.57.0", - "eslint-plugin-react": "^7.34.1", "husky": "^9.0.11", "spicetify-creator": "^1.0.17", - "typescript": "^5.4.3", - "typescript-eslint": "^7.8.0" + "typescript": "^5.4.3" }, "dependencies": { + "@biomejs/biome": "^1.7.3", "chroma-js": "^2.4.2", "i18next": "^23.11.3", "i18next-browser-languagedetector": "^7.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d423e11..98e53372 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@biomejs/biome': + specifier: ^1.7.3 + version: 1.7.3 chroma-js: specifier: ^2.4.2 version: 2.4.2 @@ -22,13 +25,13 @@ importers: version: 1.29.0 react-dropdown: specifier: ^1.11.0 - version: 1.11.0(react-dom@18.2.0)(react@18.2.0) + version: 1.11.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-i18next: specifier: ^14.1.0 - version: 14.1.0(i18next@23.11.3)(react-dom@18.2.0)(react@18.2.0) + version: 14.1.0(i18next@23.11.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-simple-code-editor: specifier: ^0.13.1 - version: 0.13.1(react-dom@18.2.0)(react@18.2.0) + version: 0.13.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) semver: specifier: ^7.6.0 version: 7.6.0 @@ -45,21 +48,9 @@ importers: '@types/semver': specifier: ^7.5.8 version: 7.5.8 - '@typescript-eslint/eslint-plugin': - specifier: ^7.8.0 - version: 7.8.0(@typescript-eslint/parser@7.8.0)(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/parser': - specifier: ^7.8.0 - version: 7.8.0(eslint@8.57.0)(typescript@5.4.3) copyfiles: specifier: ^2.4.1 version: 2.4.1 - eslint: - specifier: ^8.57.0 - version: 8.57.0 - eslint-plugin-react: - specifier: ^7.34.1 - version: 7.34.1(eslint@8.57.0) husky: specifier: ^9.0.11 version: 9.0.11 @@ -69,16 +60,9 @@ importers: typescript: specifier: ^5.4.3 version: 5.4.3 - typescript-eslint: - specifier: ^7.8.0 - version: 7.8.0(eslint@8.57.0)(typescript@5.4.3) packages: - '@aashutoshrathi/word-wrap@1.2.6': - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} - '@adobe/css-tools@4.2.0': resolution: {integrity: sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==} @@ -90,63 +74,68 @@ packages: resolution: {integrity: sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==} engines: {node: '>=6.9.0'} - '@esbuild/linux-loong64@0.14.54': - resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.10.0': - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@biomejs/biome@1.7.3': + resolution: {integrity: sha512-ogFQI+fpXftr+tiahA6bIXwZ7CSikygASdqMtH07J2cUzrpjyTMVc9Y97v23c7/tL1xCZhM+W9k4hYIBm7Q6cQ==} + engines: {node: '>=14.21.3'} + hasBin: true - '@eslint-community/regexpp@4.9.1': - resolution: {integrity: sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@biomejs/cli-darwin-arm64@1.7.3': + resolution: {integrity: sha512-eDvLQWmGRqrPIRY7AIrkPHkQ3visEItJKkPYSHCscSDdGvKzYjmBJwG1Gu8+QC5ed6R7eiU63LEC0APFBobmfQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@biomejs/cli-darwin-x64@1.7.3': + resolution: {integrity: sha512-JXCaIseKRER7dIURsVlAJacnm8SG5I0RpxZ4ya3dudASYUc68WGl4+FEN03ABY3KMIq7hcK1tzsJiWlmXyosZg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@biomejs/cli-linux-arm64-musl@1.7.3': + resolution: {integrity: sha512-c8AlO45PNFZ1BYcwaKzdt46kYbuP6xPGuGQ6h4j3XiEDpyseRRUy/h+6gxj07XovmyxKnSX9GSZ6nVbZvcVUAw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} + '@biomejs/cli-linux-arm64@1.7.3': + resolution: {integrity: sha512-phNTBpo7joDFastnmZsFjYcDYobLTx4qR4oPvc9tJ486Bd1SfEVPHEvJdNJrMwUQK56T+TRClOQd/8X1nnjA9w==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} + '@biomejs/cli-linux-x64-musl@1.7.3': + resolution: {integrity: sha512-UdEHKtYGWEX3eDmVWvQeT+z05T9/Sdt2+F/7zmMOFQ7boANeX8pcO6EkJPK3wxMudrApsNEKT26rzqK6sZRTRA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] - '@humanwhocodes/object-schema@2.0.2': - resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + '@biomejs/cli-linux-x64@1.7.3': + resolution: {integrity: sha512-vnedYcd5p4keT3iD48oSKjOIRPYcjSNNbd8MO1bKo9ajg3GwQXZLAH+0Cvlr+eMsO67/HddWmscSQwTFrC/uPA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@biomejs/cli-win32-arm64@1.7.3': + resolution: {integrity: sha512-unNCDqUKjujYkkSxs7gFIfdasttbDC4+z0kYmcqzRk6yWVoQBL4dNLcCbdnJS+qvVDNdI9rHp2NwpQ0WAdla4Q==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@biomejs/cli-win32-x64@1.7.3': + resolution: {integrity: sha512-ZmByhbrnmz/UUFYB622CECwhKIPjJLLPr5zr3edhu04LzbfcOrz16VYeNq5dpO1ADG70FORhAJkaIGdaVBG00w==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@esbuild/linux-loong64@0.14.54': + resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] '@types/chroma-js@2.4.4': resolution: {integrity: sha512-/DTccpHTaKomqussrn+ciEvfW4k6NAHzNzs/sts1TCqg333qNxOhy8TNIoQCmbGG3Tl8KdEhkGAssb1n3mTXiQ==} - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/prop-types@15.7.8': resolution: {integrity: sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==} @@ -162,80 +151,6 @@ packages: '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - '@typescript-eslint/eslint-plugin@7.8.0': - resolution: {integrity: sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@7.8.0': - resolution: {integrity: sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/scope-manager@7.8.0': - resolution: {integrity: sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/type-utils@7.8.0': - resolution: {integrity: sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/types@7.8.0': - resolution: {integrity: sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/typescript-estree@7.8.0': - resolution: {integrity: sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@7.8.0': - resolution: {integrity: sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - - '@typescript-eslint/visitor-keys@7.8.0': - resolution: {integrity: sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@ungap/structured-clone@1.2.0': - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.10.0: - resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} - engines: {node: '>=0.4.0'} - hasBin: true - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -248,50 +163,6 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - array-buffer-byte-length@1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} - - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} - - array-includes@3.1.7: - resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} - engines: {node: '>= 0.4'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} - - array.prototype.toreversed@1.1.2: - resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} - - array.prototype.tosorted@1.1.3: - resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==} - - arraybuffer.prototype.slice@1.0.2: - resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} - at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} @@ -303,14 +174,6 @@ packages: peerDependencies: postcss: ^8.1.0 - available-typed-arrays@1.0.5: - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} - engines: {node: '>= 0.4'} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -321,9 +184,6 @@ packages: brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -333,17 +193,6 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - caniuse-lite@1.0.30001549: resolution: {integrity: sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==} @@ -388,10 +237,6 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -404,18 +249,6 @@ packages: resolution: {integrity: sha512-YGZxdTTL9lmLkCUTpg4j0zQ7IhRB5ZmqNBbGCl3Tg6MP/d5/6sY7L5mmTjzbc6JKgVZYiqTQTNhPFsbXNGlRaA==} engines: {node: '>=0.8'} - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} - debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -433,33 +266,6 @@ packages: supports-color: optional: true - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - define-data-property@1.1.1: - resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} - engines: {node: '>= 0.4'} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - electron-to-chromium@1.4.556: resolution: {integrity: sha512-6RPN0hHfzDU8D56E72YkDvnLw5Cj2NMXZGg3UkgyoHxjVhG99KZpsKgBWMmTy0Ei89xwan+rbRsVB9yzATmYzQ==} @@ -470,52 +276,6 @@ packages: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true - es-abstract@1.22.2: - resolution: {integrity: sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==} - engines: {node: '>= 0.4'} - - es-abstract@1.22.5: - resolution: {integrity: sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==} - engines: {node: '>= 0.4'} - - es-abstract@1.23.2: - resolution: {integrity: sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-iterator-helpers@1.0.18: - resolution: {integrity: sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.0.1: - resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} - - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - esbuild-android-64@0.14.54: resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==} engines: {node: '>=12'} @@ -656,49 +416,6 @@ packages: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - eslint-plugin-react@7.34.1: - resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - expand-tilde@1.2.2: resolution: {integrity: sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q==} engines: {node: '>=0.10.0'} @@ -711,26 +428,6 @@ packages: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.1: - resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fastq@1.15.0: - resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} - - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -743,20 +440,6 @@ packages: resolution: {integrity: sha512-0rnQWcFwZr7eO0513HahrWafsc3CTFioEB7DRiEYCUM/70QXSY8f3mCST17HXLcPvEhzH/Ty/Bxd72ZZsr/yvw==} engines: {node: '>=0.10.0'} - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@3.1.1: - resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} - engines: {node: '>=12.0.0'} - - flatted@3.2.9: - resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} - - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -776,16 +459,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - generic-names@4.0.0: resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==} @@ -793,29 +466,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.2.1: - resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} - - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - - get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} - engines: {node: '>= 0.4'} - - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -827,68 +481,17 @@ packages: resolution: {integrity: sha512-gOPiyxcD9dJGCEArAhF4Hd0BAqvAe/JzERP7tYumE4yIkmIedPUVXcJFWbV3/p/ovIIvKjkrTk+f1UVkq7vvbw==} engines: {node: '>=0.10.0'} - globals@13.23.0: - resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} - engines: {node: '>=8'} - - globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - - has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} - engines: {node: '>= 0.4'} - - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - has@1.0.4: resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} engines: {node: '>= 0.4.0'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - homedir-polyfill@1.0.3: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} engines: {node: '>=0.10.0'} @@ -920,14 +523,6 @@ packages: peerDependencies: postcss: ^8.1.0 - ignore@5.2.4: - resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} - engines: {node: '>= 4'} - - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - image-size@0.5.5: resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} engines: {node: '>=0.10.0'} @@ -936,14 +531,6 @@ packages: immutable@4.3.4: resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} @@ -953,54 +540,16 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - internal-slot@1.0.5: - resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} - engines: {node: '>= 0.4'} - - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} - - is-array-buffer@3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} - - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} - - is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} - - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - is-buffer@1.1.6: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} - - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - is-extendable@0.1.1: resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} @@ -1009,83 +558,18 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - - is-negative-zero@2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} - engines: {node: '>= 0.4'} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - - is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - - is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} - - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} - - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.12: - resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - - is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} - - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - - is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} - is-what@3.14.1: resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} @@ -1099,41 +583,15 @@ packages: isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kind-of@3.2.2: resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} engines: {node: '>=0.10.0'} @@ -1147,24 +605,13 @@ packages: engines: {node: '>=6'} hasBin: true - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - loader-utils@3.2.1: resolution: {integrity: sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==} engines: {node: '>= 12.13.0'} - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -1177,14 +624,6 @@ packages: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} - mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} @@ -1193,10 +632,6 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} - engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -1216,9 +651,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - needle@3.2.0: resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==} engines: {node: '>= 4.4.x'} @@ -1238,66 +670,13 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.0: - resolution: {integrity: sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==} - - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} - engines: {node: '>= 0.4'} - - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - - object.entries@1.1.7: - resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.7: - resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} - engines: {node: '>= 0.4'} - - object.hasown@1.1.3: - resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} - - object.values@1.1.7: - resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} - engines: {node: '>= 0.4'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} - engines: {node: '>= 0.8.0'} - os-homedir@1.0.2: resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} engines: {node: '>=0.10.0'} - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - parse-node-version@1.0.1: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} @@ -1306,25 +685,13 @@ packages: resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} engines: {node: '>=0.10.0'} - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} + engines: {node: '>=0.10.0'} path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -1336,10 +703,6 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - postcss-modules-extract-imports@3.0.0: resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} @@ -1380,10 +743,6 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - prismjs@1.29.0: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} @@ -1391,19 +750,9 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - punycode@2.3.0: - resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} - engines: {node: '>=6'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-dom@18.2.0: resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -1428,9 +777,6 @@ packages: react-native: optional: true - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-simple-code-editor@0.13.1: resolution: {integrity: sha512-XYeVwRZwgyKtjNIYcAEgg2FaQcCZwhbarnkJIV20U2wkCU9q/CPFBo8nRXrK4GXUz3AvbqZFsZRrpUTkqqEYyQ==} peerDependencies: @@ -1451,21 +797,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - reflect.getprototypeof@1.0.4: - resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} - engines: {node: '>= 0.4'} - regenerator-runtime@0.14.0: resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} - regexp.prototype.flags@1.5.1: - resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} - engines: {node: '>= 0.4'} - - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -1478,47 +812,17 @@ packages: resolution: {integrity: sha512-9RXicAgDvLD272hZ3HwJv9MJUGxCBRRwwSBRdOGWgcO03MtC9UTGC6XG1VbS4T5MvDrb+tVZx2RhZ90uk3uczg==} engines: {node: '>=0.10.0'} - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true - resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-array-concat@1.0.1: - resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} - engines: {node: '>=0.4'} - - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} - safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - safe-regex-test@1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} - - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -1540,42 +844,15 @@ packages: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.6.0: resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} engines: {node: '>=10'} hasBin: true - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.1: - resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} - engines: {node: '>= 0.4'} - set-getter@0.1.1: resolution: {integrity: sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==} engines: {node: '>=0.10.0'} - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -1599,26 +876,6 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string.prototype.matchall@4.0.10: - resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} - - string.prototype.trim@1.2.8: - resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} - engines: {node: '>= 0.4'} - - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.7: - resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} - - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} - - string.prototype.trimstart@1.0.7: - resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} - string_decoder@0.10.31: resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} @@ -1629,10 +886,6 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - stylus@0.60.0: resolution: {integrity: sha512-j2pBgEwzCu05yCuY4cmyp0FtPQQFBBAGB7TY7QaNl7eztiHwkxzwvIp5vjZJND/a1JNOka+ZW9ewVPFZpI3pcA==} hasBin: true @@ -1645,9 +898,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} @@ -1663,64 +913,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - typed-array-buffer@1.0.0: - resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} - engines: {node: '>= 0.4'} - - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.0: - resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.0: - resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} - - typed-array-length@1.0.5: - resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==} - engines: {node: '>= 0.4'} - - typescript-eslint@7.8.0: - resolution: {integrity: sha512-sheFG+/D8N/L7gC3WT0Q8sB97Nm573Yfr+vZFzl/4nBdYcmviBPtwGSX9TJ7wpVg28ocerKVOt+k2eGmHzcgVA==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - typescript@5.4.3: resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==} engines: {node: '>=14.17'} @@ -1731,9 +926,6 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - universalify@2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} @@ -1748,9 +940,6 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -1758,33 +947,10 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - - which-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} - engines: {node: '>= 0.4'} - - which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} - - which-typed-array@1.1.11: - resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} - engines: {node: '>= 0.4'} - - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} - which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -1811,14 +977,8 @@ packages: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - snapshots: - '@aashutoshrathi/word-wrap@1.2.6': {} - '@adobe/css-tools@4.2.0': {} '@babel/runtime@7.23.2': @@ -1829,62 +989,46 @@ snapshots: dependencies: regenerator-runtime: 0.14.0 - '@esbuild/linux-loong64@0.14.54': + '@biomejs/biome@1.7.3': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 1.7.3 + '@biomejs/cli-darwin-x64': 1.7.3 + '@biomejs/cli-linux-arm64': 1.7.3 + '@biomejs/cli-linux-arm64-musl': 1.7.3 + '@biomejs/cli-linux-x64': 1.7.3 + '@biomejs/cli-linux-x64-musl': 1.7.3 + '@biomejs/cli-win32-arm64': 1.7.3 + '@biomejs/cli-win32-x64': 1.7.3 + + '@biomejs/cli-darwin-arm64@1.7.3': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': - dependencies: - eslint: 8.57.0 - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.10.0': {} - - '@eslint-community/regexpp@4.9.1': {} - - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.6.1 - globals: 13.23.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color + '@biomejs/cli-darwin-x64@1.7.3': + optional: true - '@eslint/js@8.57.0': {} + '@biomejs/cli-linux-arm64-musl@1.7.3': + optional: true - '@humanwhocodes/config-array@0.11.14': - dependencies: - '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color + '@biomejs/cli-linux-arm64@1.7.3': + optional: true - '@humanwhocodes/module-importer@1.0.1': {} + '@biomejs/cli-linux-x64-musl@1.7.3': + optional: true - '@humanwhocodes/object-schema@2.0.2': {} + '@biomejs/cli-linux-x64@1.7.3': + optional: true - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + '@biomejs/cli-win32-arm64@1.7.3': + optional: true - '@nodelib/fs.stat@2.0.5': {} + '@biomejs/cli-win32-x64@1.7.3': + optional: true - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.15.0 + '@esbuild/linux-loong64@0.14.54': + optional: true '@types/chroma-js@2.4.4': {} - '@types/json-schema@7.0.15': {} - '@types/prop-types@15.7.8': {} '@types/react-dom@18.2.0': @@ -1901,103 +1045,6 @@ snapshots: '@types/semver@7.5.8': {} - '@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0)(eslint@8.57.0)(typescript@5.4.3)': - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.8.0(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/type-utils': 7.8.0(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/visitor-keys': 7.8.0 - debug: 4.3.4 - eslint: 8.57.0 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.3)': - dependencies: - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.3) - '@typescript-eslint/visitor-keys': 7.8.0 - debug: 4.3.4 - eslint: 8.57.0 - typescript: 5.4.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@7.8.0': - dependencies: - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/visitor-keys': 7.8.0 - - '@typescript-eslint/type-utils@7.8.0(eslint@8.57.0)(typescript@5.4.3)': - dependencies: - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.3) - '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.3) - debug: 4.3.4 - eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@7.8.0': {} - - '@typescript-eslint/typescript-estree@7.8.0(typescript@5.4.3)': - dependencies: - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/visitor-keys': 7.8.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.4 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@7.8.0(eslint@8.57.0)(typescript@5.4.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.3) - eslint: 8.57.0 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/visitor-keys@7.8.0': - dependencies: - '@typescript-eslint/types': 7.8.0 - eslint-visitor-keys: 3.4.3 - - '@ungap/structured-clone@1.2.0': {} - - acorn-jsx@5.3.2(acorn@8.10.0): - dependencies: - acorn: 8.10.0 - - acorn@8.10.0: {} - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ansi-regex@5.0.1: {} ansi-styles@4.3.0: @@ -2009,87 +1056,6 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - argparse@2.0.1: {} - - array-buffer-byte-length@1.0.0: - dependencies: - call-bind: 1.0.2 - is-array-buffer: 3.0.2 - - array-buffer-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 - - array-includes@3.1.7: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - get-intrinsic: 1.2.1 - is-string: 1.0.7 - - array-union@2.1.0: {} - - array.prototype.findlast@1.2.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.2 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - - array.prototype.flat@1.3.2: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - es-shim-unscopables: 1.0.0 - - array.prototype.flatmap@1.3.2: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - es-shim-unscopables: 1.0.0 - - array.prototype.toreversed@1.1.2: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - es-shim-unscopables: 1.0.0 - - array.prototype.tosorted@1.1.3: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.5 - es-errors: 1.3.0 - es-shim-unscopables: 1.0.2 - - arraybuffer.prototype.slice@1.0.2: - dependencies: - array-buffer-byte-length: 1.0.0 - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - get-intrinsic: 1.2.1 - is-array-buffer: 3.0.2 - is-shared-array-buffer: 1.0.2 - - arraybuffer.prototype.slice@1.0.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.2 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 - at-least-node@1.0.0: {} autoprefixer@10.4.16(postcss@8.4.31): @@ -2102,12 +1068,6 @@ snapshots: postcss: 8.4.31 postcss-value-parser: 4.2.0 - available-typed-arrays@1.0.5: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.0.0 - balanced-match@1.0.2: {} binary-extensions@2.2.0: {} @@ -2117,10 +1077,6 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - braces@3.0.2: dependencies: fill-range: 7.0.1 @@ -2132,21 +1088,6 @@ snapshots: node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.22.1) - call-bind@1.0.2: - dependencies: - function-bind: 1.1.2 - get-intrinsic: 1.2.1 - - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - - callsites@3.1.0: {} - caniuse-lite@1.0.30001549: {} chalk@4.1.2: @@ -2204,12 +1145,6 @@ snapshots: core-util-is@1.0.3: {} - cross-spawn@7.0.3: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - cssesc@3.0.0: {} csstype@3.1.2: {} @@ -2219,24 +1154,6 @@ snapshots: find-pkg: 0.1.2 fs-exists-sync: 0.1.0 - data-view-buffer@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-offset@1.0.0: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - debug@3.2.7: dependencies: ms: 2.1.3 @@ -2246,38 +1163,6 @@ snapshots: dependencies: ms: 2.1.2 - deep-is@0.1.4: {} - - define-data-property@1.1.1: - dependencies: - get-intrinsic: 1.2.1 - gopd: 1.0.1 - has-property-descriptors: 1.0.0 - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.1 - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - electron-to-chromium@1.4.556: {} emoji-regex@8.0.0: {} @@ -2287,194 +1172,6 @@ snapshots: prr: 1.0.1 optional: true - es-abstract@1.22.2: - dependencies: - array-buffer-byte-length: 1.0.0 - arraybuffer.prototype.slice: 1.0.2 - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - es-set-tostringtag: 2.0.1 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.1 - get-symbol-description: 1.0.0 - globalthis: 1.0.3 - gopd: 1.0.1 - has: 1.0.4 - has-property-descriptors: 1.0.0 - has-proto: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - is-array-buffer: 3.0.2 - is-callable: 1.2.7 - is-negative-zero: 2.0.2 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - is-string: 1.0.7 - is-typed-array: 1.1.12 - is-weakref: 1.0.2 - object-inspect: 1.13.0 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.5.1 - safe-array-concat: 1.0.1 - safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.8 - string.prototype.trimend: 1.0.7 - string.prototype.trimstart: 1.0.7 - typed-array-buffer: 1.0.0 - typed-array-byte-length: 1.0.0 - typed-array-byte-offset: 1.0.0 - typed-array-length: 1.0.4 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.11 - - es-abstract@1.22.5: - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - es-define-property: 1.0.0 - es-errors: 1.3.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 - globalthis: 1.0.3 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.1 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.8 - string.prototype.trimend: 1.0.7 - string.prototype.trimstart: 1.0.7 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.5 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - - es-abstract@1.23.2: - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 - globalthis: 1.0.3 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.1 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 - string.prototype.trimstart: 1.0.7 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.5 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - - es-errors@1.3.0: {} - - es-iterator-helpers@1.0.18: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.2 - es-errors: 1.3.0 - es-set-tostringtag: 2.0.3 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - globalthis: 1.0.3 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - iterator.prototype: 1.1.2 - safe-array-concat: 1.1.2 - - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.0.1: - dependencies: - get-intrinsic: 1.2.1 - has: 1.0.4 - has-tostringtag: 1.0.0 - - es-set-tostringtag@2.0.3: - dependencies: - get-intrinsic: 1.2.4 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.0.0: - dependencies: - has: 1.0.4 - - es-shim-unscopables@1.0.2: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.2.1: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - esbuild-android-64@0.14.54: optional: true @@ -2575,98 +1272,6 @@ snapshots: escalade@3.1.1: {} - escape-string-regexp@4.0.0: {} - - eslint-plugin-react@7.34.1(eslint@8.57.0): - dependencies: - array-includes: 3.1.7 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.2 - array.prototype.toreversed: 1.1.2 - array.prototype.tosorted: 1.1.3 - doctrine: 2.1.0 - es-iterator-helpers: 1.0.18 - eslint: 8.57.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.7 - object.fromentries: 2.0.7 - object.hasown: 1.1.3 - object.values: 1.1.7 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.10 - - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint@8.57.0: - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.9.1 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.23.0 - graphemer: 1.4.0 - ignore: 5.2.4 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.3 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - - espree@9.6.1: - dependencies: - acorn: 8.10.0 - acorn-jsx: 5.3.2(acorn@8.10.0) - eslint-visitor-keys: 3.4.3 - - esquery@1.5.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - - esutils@2.0.3: {} - expand-tilde@1.2.2: dependencies: os-homedir: 1.0.2 @@ -2675,31 +1280,9 @@ snapshots: dependencies: homedir-polyfill: 1.0.3 - extend-shallow@2.0.1: - dependencies: - is-extendable: 0.1.1 - - fast-deep-equal@3.1.3: {} - - fast-glob@3.3.1: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fastq@1.15.0: - dependencies: - reusify: 1.0.4 - - file-entry-cache@6.0.1: + extend-shallow@2.0.1: dependencies: - flat-cache: 3.1.1 + is-extendable: 0.1.1 fill-range@7.0.1: dependencies: @@ -2714,23 +1297,6 @@ snapshots: dependencies: find-file-up: 0.1.3 - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@3.1.1: - dependencies: - flatted: 3.2.9 - keyv: 4.5.4 - rimraf: 3.0.2 - - flatted@3.2.9: {} - - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 - fraction.js@4.3.7: {} fs-exists-sync@0.1.0: {} @@ -2747,57 +1313,16 @@ snapshots: fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - - function.prototype.name@1.1.6: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - functions-have-names: 1.2.3 - - functions-have-names@1.2.3: {} - generic-names@4.0.0: dependencies: loader-utils: 3.2.1 get-caller-file@2.0.5: {} - get-intrinsic@1.2.1: - dependencies: - function-bind: 1.1.2 - has: 1.0.4 - has-proto: 1.0.1 - has-symbols: 1.0.3 - - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - - get-symbol-description@1.0.0: - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - - get-symbol-description@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -2819,63 +1344,12 @@ snapshots: is-windows: 0.2.0 which: 1.3.1 - globals@13.23.0: - dependencies: - type-fest: 0.20.2 - - globalthis@1.0.3: - dependencies: - define-properties: 1.2.1 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.1 - ignore: 5.3.1 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.1 - graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - - has-bigints@1.0.2: {} - has-flag@4.0.0: {} - has-property-descriptors@1.0.0: - dependencies: - get-intrinsic: 1.2.1 - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.0 - - has-proto@1.0.1: {} - - has-proto@1.0.3: {} - - has-symbols@1.0.3: {} - - has-tostringtag@1.0.0: - dependencies: - has-symbols: 1.0.3 - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.0.3 - has@1.0.4: {} - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - homedir-polyfill@1.0.3: dependencies: parse-passwd: 1.0.0 @@ -2905,22 +1379,11 @@ snapshots: dependencies: postcss: 8.4.31 - ignore@5.2.4: {} - - ignore@5.3.1: {} - image-size@0.5.5: optional: true immutable@4.3.4: {} - import-fresh@3.3.0: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - inflight@1.0.6: dependencies: once: 1.4.0 @@ -2930,136 +1393,28 @@ snapshots: ini@1.3.8: {} - internal-slot@1.0.5: - dependencies: - get-intrinsic: 1.2.1 - has: 1.0.4 - side-channel: 1.0.4 - - internal-slot@1.0.7: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.0.4 - - is-array-buffer@3.0.2: - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - is-typed-array: 1.1.12 - - is-array-buffer@3.0.4: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - - is-async-function@2.0.0: - dependencies: - has-tostringtag: 1.0.0 - - is-bigint@1.0.4: - dependencies: - has-bigints: 1.0.2 - is-binary-path@2.1.0: dependencies: binary-extensions: 2.2.0 - is-boolean-object@1.1.2: - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - is-buffer@1.1.6: {} - is-callable@1.2.7: {} - is-core-module@2.13.0: dependencies: has: 1.0.4 - is-data-view@1.0.1: - dependencies: - is-typed-array: 1.1.13 - - is-date-object@1.0.5: - dependencies: - has-tostringtag: 1.0.0 - is-extendable@0.1.1: {} is-extglob@2.1.1: {} - is-finalizationregistry@1.0.2: - dependencies: - call-bind: 1.0.7 - is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.0.10: - dependencies: - has-tostringtag: 1.0.0 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-map@2.0.2: {} - - is-negative-zero@2.0.2: {} - - is-negative-zero@2.0.3: {} - - is-number-object@1.0.7: - dependencies: - has-tostringtag: 1.0.0 - is-number@7.0.0: {} - is-path-inside@3.0.3: {} - - is-regex@1.1.4: - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - - is-set@2.0.2: {} - - is-shared-array-buffer@1.0.2: - dependencies: - call-bind: 1.0.2 - - is-shared-array-buffer@1.0.3: - dependencies: - call-bind: 1.0.7 - - is-string@1.0.7: - dependencies: - has-tostringtag: 1.0.0 - - is-symbol@1.0.4: - dependencies: - has-symbols: 1.0.3 - - is-typed-array@1.1.12: - dependencies: - which-typed-array: 1.1.11 - - is-typed-array@1.1.13: - dependencies: - which-typed-array: 1.1.15 - - is-weakmap@2.0.1: {} - - is-weakref@1.0.2: - dependencies: - call-bind: 1.0.2 - - is-weakset@2.0.2: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - is-what@3.14.1: {} is-windows@0.2.0: {} @@ -3068,47 +1423,16 @@ snapshots: isarray@1.0.0: {} - isarray@2.0.5: {} - isexe@2.0.0: {} - iterator.prototype@1.1.2: - dependencies: - define-properties: 1.2.1 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.4 - set-function-name: 2.0.1 - js-tokens@4.0.0: {} - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 - - json-buffer@3.0.1: {} - - json-schema-traverse@0.4.1: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - jsonfile@6.1.0: dependencies: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.11 - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.7 - array.prototype.flat: 1.3.2 - object.assign: 4.1.4 - object.values: 1.1.7 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - kind-of@3.2.2: dependencies: is-buffer: 1.1.6 @@ -3133,21 +1457,10 @@ snapshots: transitivePeerDependencies: - supports-color - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - loader-utils@3.2.1: {} - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - lodash.camelcase@4.3.0: {} - lodash.merge@4.6.2: {} - loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -3162,13 +1475,6 @@ snapshots: semver: 5.7.2 optional: true - merge2@1.4.1: {} - - micromatch@4.0.5: - dependencies: - braces: 3.0.2 - picomatch: 2.3.1 - mime@1.6.0: optional: true @@ -3176,10 +1482,6 @@ snapshots: dependencies: brace-expansion: 1.1.11 - minimatch@9.0.4: - dependencies: - brace-expansion: 2.0.1 - minimist@1.2.8: {} mkdirp@1.0.4: {} @@ -3191,8 +1493,6 @@ snapshots: nanoid@3.3.6: {} - natural-compare@1.4.0: {} - needle@3.2.0: dependencies: debug: 3.2.7 @@ -3213,92 +1513,20 @@ snapshots: normalize-range@0.1.2: {} - object-assign@4.1.1: {} - - object-inspect@1.13.0: {} - - object-inspect@1.13.1: {} - - object-keys@1.1.1: {} - - object.assign@4.1.4: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - - object.assign@4.1.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - - object.entries@1.1.7: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - - object.fromentries@2.0.7: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - - object.hasown@1.1.3: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.22.2 - - object.values@1.1.7: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - once@1.4.0: dependencies: wrappy: 1.0.2 - optionator@0.9.3: - dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - os-homedir@1.0.2: {} - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - parse-node-version@1.0.1: {} parse-passwd@1.0.0: {} - path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} - path-parse@1.0.7: {} - path-type@4.0.0: {} - picocolors@1.0.0: {} picomatch@2.3.1: {} @@ -3306,8 +1534,6 @@ snapshots: pify@4.0.1: optional: true - possible-typed-array-names@1.0.0: {} - postcss-modules-extract-imports@3.0.0(postcss@8.4.31): dependencies: postcss: 8.4.31 @@ -3354,48 +1580,35 @@ snapshots: picocolors: 1.0.0 source-map-js: 1.0.2 - prelude-ls@1.2.1: {} - prismjs@1.29.0: {} process-nextick-args@2.0.1: {} - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - prr@1.0.1: optional: true - punycode@2.3.0: {} - - queue-microtask@1.2.3: {} - react-dom@18.2.0(react@18.2.0): dependencies: loose-envify: 1.4.0 react: 18.2.0 scheduler: 0.23.0 - react-dropdown@1.11.0(react-dom@18.2.0)(react@18.2.0): + react-dropdown@1.11.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: classnames: 2.3.2 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-i18next@14.1.0(i18next@23.11.3)(react-dom@18.2.0)(react@18.2.0): + react-i18next@14.1.0(i18next@23.11.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime': 7.24.1 html-parse-stringify: 3.0.1 i18next: 23.11.3 react: 18.2.0 + optionalDependencies: react-dom: 18.2.0(react@18.2.0) - react-is@16.13.1: {} - - react-simple-code-editor@0.13.1(react-dom@18.2.0)(react@18.2.0): + react-simple-code-editor@0.13.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -3425,30 +1638,8 @@ snapshots: dependencies: picomatch: 2.3.1 - reflect.getprototypeof@1.0.4: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.2 - get-intrinsic: 1.2.4 - globalthis: 1.0.3 - which-builtin-type: 1.1.3 - regenerator-runtime@0.14.0: {} - regexp.prototype.flags@1.5.1: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - set-function-name: 2.0.1 - - regexp.prototype.flags@1.5.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-errors: 1.3.0 - set-function-name: 2.0.1 - require-directory@2.1.1: {} resolve-dir@0.1.1: @@ -3466,58 +1657,18 @@ snapshots: lazy-cache: 2.0.2 resolve: 1.22.8 - resolve-from@4.0.0: {} - resolve@1.22.8: dependencies: is-core-module: 2.13.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.5: - dependencies: - is-core-module: 2.13.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.0.4: {} - rimraf@3.0.2: dependencies: glob: 7.2.3 - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-array-concat@1.0.1: - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - has-symbols: 1.0.3 - isarray: 2.0.5 - - safe-array-concat@1.1.2: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - isarray: 2.0.5 - safe-buffer@5.1.2: {} - safe-regex-test@1.0.0: - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - is-regex: 1.1.4 - - safe-regex-test@1.0.3: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-regex: 1.1.4 - safer-buffer@2.1.2: optional: true @@ -3539,45 +1690,14 @@ snapshots: semver@5.7.2: optional: true - semver@6.3.1: {} - semver@7.6.0: dependencies: lru-cache: 6.0.0 - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.1: - dependencies: - define-data-property: 1.1.1 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.0 - set-getter@0.1.1: dependencies: to-object-path: 0.3.0 - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - side-channel@1.0.4: - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - object-inspect: 1.13.0 - - slash@3.0.0: {} - source-map-js@1.0.2: {} source-map@0.6.1: {} @@ -3608,49 +1728,6 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string.prototype.matchall@4.0.10: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - get-intrinsic: 1.2.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - regexp.prototype.flags: 1.5.1 - set-function-name: 2.0.1 - side-channel: 1.0.4 - - string.prototype.trim@1.2.8: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - - string.prototype.trim@1.2.9: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.2 - es-object-atoms: 1.0.0 - - string.prototype.trimend@1.0.7: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - - string.prototype.trimend@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string.prototype.trimstart@1.0.7: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - string_decoder@0.10.31: {} string_decoder@1.1.1: @@ -3661,8 +1738,6 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-json-comments@3.1.1: {} - stylus@0.60.0: dependencies: '@adobe/css-tools': 4.2.0 @@ -3679,8 +1754,6 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - text-table@0.2.0: {} - through2@2.0.5: dependencies: readable-stream: 2.3.8 @@ -3698,98 +1771,12 @@ snapshots: dependencies: is-number: 7.0.0 - ts-api-utils@1.3.0(typescript@5.4.3): - dependencies: - typescript: 5.4.3 - tslib@2.6.2: {} - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-fest@0.20.2: {} - - typed-array-buffer@1.0.0: - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - is-typed-array: 1.1.12 - - typed-array-buffer@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-typed-array: 1.1.13 - - typed-array-byte-length@1.0.0: - dependencies: - call-bind: 1.0.2 - for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 - - typed-array-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - - typed-array-byte-offset@1.0.0: - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 - - typed-array-byte-offset@1.0.2: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - - typed-array-length@1.0.4: - dependencies: - call-bind: 1.0.2 - for-each: 0.3.3 - is-typed-array: 1.1.12 - - typed-array-length@1.0.5: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - possible-typed-array-names: 1.0.0 - - typescript-eslint@7.8.0(eslint@8.57.0)(typescript@5.4.3): - dependencies: - '@typescript-eslint/eslint-plugin': 7.8.0(@typescript-eslint/parser@7.8.0)(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/parser': 7.8.0(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.3) - eslint: 8.57.0 - typescript: 5.4.3 - transitivePeerDependencies: - - supports-color - typescript@5.4.3: {} uglify-js@3.17.4: {} - unbox-primitive@1.0.2: - dependencies: - call-bind: 1.0.2 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - universalify@2.0.0: {} untildify@4.0.0: {} @@ -3800,68 +1787,14 @@ snapshots: escalade: 3.1.1 picocolors: 1.0.0 - uri-js@4.4.1: - dependencies: - punycode: 2.3.0 - util-deprecate@1.0.2: {} void-elements@3.1.0: {} - which-boxed-primitive@1.0.2: - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - - which-builtin-type@1.1.3: - dependencies: - function.prototype.name: 1.1.6 - has-tostringtag: 1.0.0 - is-async-function: 2.0.0 - is-date-object: 1.0.5 - is-finalizationregistry: 1.0.2 - is-generator-function: 1.0.10 - is-regex: 1.1.4 - is-weakref: 1.0.2 - isarray: 2.0.5 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.11 - - which-collection@1.0.1: - dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 - - which-typed-array@1.1.11: - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 - - which-typed-array@1.1.15: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - which@1.3.1: dependencies: isexe: 2.0.0 - which@2.0.2: - dependencies: - isexe: 2.0.0 - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -3887,5 +1820,3 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 - - yocto-queue@0.1.0: {} diff --git a/resources/blacklist.json b/resources/blacklist.json index 1761d9cd..44781b4a 100644 --- a/resources/blacklist.json +++ b/resources/blacklist.json @@ -1,3 +1,3 @@ { - "repos": ["https://github.com/FoxRefire/spiceDL"] + "repos": ["https://github.com/FoxRefire/spiceDL"] } diff --git a/resources/snippets.json b/resources/snippets.json index 6d277fd4..fb7b7768 100644 --- a/resources/snippets.json +++ b/resources/snippets.json @@ -342,39 +342,39 @@ "preview": "resources/assets/snippets/pokemon-adventure.png" }, { - "title": "Hide Friend Activity Button 2024", - "description": "Hides the Friend Activity button", - "code": "[aria-label='Friend Activity'] {display:none;}", - "preview": "resources/assets/snippets/Hide-Friend-Activity-Button.png" + "title": "Hide Friend Activity Button 2024", + "description": "Hides the Friend Activity button", + "code": "[aria-label='Friend Activity'] {display:none;}", + "preview": "resources/assets/snippets/Hide-Friend-Activity-Button.png" }, { - "title": "Hide What's New Button", - "description": "Hides the What's New button", - "code": "[aria-label=\"What's New\"] {display:none;}", - "preview": "resources/assets/snippets/Hide-Whats-New-Button.png" + "title": "Hide What's New Button", + "description": "Hides the What's New button", + "code": "[aria-label=\"What's New\"] {display:none;}", + "preview": "resources/assets/snippets/Hide-Whats-New-Button.png" }, { - "title": "Hide Audiobooks Button", - "description": "Hides the Audiobook button on the home page", - "code": "button[aria-label='Audiobooks'] {display:none;}", - "preview": "resources/assets/snippets/Hide-Audiobooks-Button.png" + "title": "Hide Audiobooks Button", + "description": "Hides the Audiobook button on the home page", + "code": "button[aria-label='Audiobooks'] {display:none;}", + "preview": "resources/assets/snippets/Hide-Audiobooks-Button.png" }, { - "title": "Hide Podcast Button", - "description": "Hides the Podcast button on the home page", - "code": "button[aria-label='Podcasts'] {display:none;}", - "preview": "resources/assets/snippets/Hide-Podcast-Button.png" + "title": "Hide Podcast Button", + "description": "Hides the Podcast button on the home page", + "code": "button[aria-label='Podcasts'] {display:none;}", + "preview": "resources/assets/snippets/Hide-Podcast-Button.png" }, { - "title": "Hide Recently Played Sections", - "description": "Hides the Recently Played sections on the home page", - "code": ".view-homeShortcutsGrid-shortcuts, section[aria-label='Recently played'] {display:none;}", - "preview": "resources/assets/snippets/Hide-Recently-Played.png" + "title": "Hide Recently Played Sections", + "description": "Hides the Recently Played sections on the home page", + "code": ".view-homeShortcutsGrid-shortcuts, section[aria-label='Recently played'] {display:none;}", + "preview": "resources/assets/snippets/Hide-Recently-Played.png" }, { - "title": "More Visible Unplayable Tracks", - "description": "Changes the background of unplayable tracks to make it easier to spot them in large playlists.", - "code": ".main-trackList-disabled{background:#f004}.main-trackList-disabled:focus-within,.main-trackList-disabled:hover{background:#f005}.main-trackList-disabled.main-trackList-selected,.main-trackList-disabled.main-trackList-selected:hover{background:#f006}", - "preview": "resources/assets/snippets/More-Visible-Unplayable-Tracks" + "title": "More Visible Unplayable Tracks", + "description": "Changes the background of unplayable tracks to make it easier to spot them in large playlists.", + "code": ".main-trackList-disabled{background:#f004}.main-trackList-disabled:focus-within,.main-trackList-disabled:hover{background:#f005}.main-trackList-disabled.main-trackList-selected,.main-trackList-disabled.main-trackList-selected:hover{background:#f006}", + "preview": "resources/assets/snippets/More-Visible-Unplayable-Tracks" } ] diff --git a/src/app.tsx b/src/app.tsx index 3c56ab6c..4bb21862 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,15 +1,15 @@ -import React from "react"; import i18n, { t } from "i18next"; -import { withTranslation, initReactI18next } from "react-i18next"; import LanguageDetector from "i18next-browser-languagedetector"; +import React from "react"; +import { initReactI18next, withTranslation } from "react-i18next"; import "./styles/styles.scss"; -import locales from "./resources/locales"; -import { Config, TabItemConfig } from "./types/marketplace-types"; -import { ALL_TABS, LOCALSTORAGE_KEYS, CUSTOM_APP_PATH } from "./constants"; import Grid from "./components/Grid"; import ReadmePage from "./components/ReadmePage"; +import { ALL_TABS, CUSTOM_APP_PATH, LOCALSTORAGE_KEYS } from "./constants"; import { getLocalStorageDataFromKey } from "./logic/Utils"; +import locales from "./resources/locales"; +import type { Config, TabItemConfig } from "./types/marketplace-types"; i18n .use(initReactI18next) // passes i18n down to react-i18next @@ -18,24 +18,27 @@ i18n // the translations resources: locales, detection: { - order: [ "navigator", "htmlTag" ], + order: ["navigator", "htmlTag"] }, // lng: "en", // if you're using a language detector, do not define the lng option fallbackLng: "en", interpolation: { - escapeValue: false, // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape - }, + escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape + } }); -class App extends React.Component<{ - t: (key: string) => string, -}, { - count: number, - CONFIG: Config, -}> { +class App extends React.Component< + { + t: (key: string) => string; + }, + { + count: number; + CONFIG: Config; + } +> { state = { count: 0, - CONFIG: {} as Config, + CONFIG: {} as Config }; CONFIG: Config; @@ -49,9 +52,11 @@ class App extends React.Component<{ tabs = JSON.parse(tabsString); if (!Array.isArray(tabs)) { throw new Error("Could not parse marketplace tabs key"); - } else if (tabs.length === 0) { + } + if (tabs.length === 0) { throw new Error("Empty marketplace tabs key"); - } else if (tabs.filter(tab => !tab).length > 0) { + } + if (tabs.filter((tab) => !tab).length > 0) { throw new Error("Falsey marketplace tabs key"); } } catch { @@ -94,19 +99,19 @@ class App extends React.Component<{ // I was considering adding watchers as "followers" but it looks like the value is a duplicate // of stargazers, and the subscribers_count isn't returned in the main API call we make // https://github.community/t/bug-watchers-count-is-the-duplicate-of-stargazers-count/140865/4 - followers: JSON.parse(getLocalStorageDataFromKey("marketplace:followers", false)), + followers: JSON.parse(getLocalStorageDataFromKey("marketplace:followers", false)) }, tabs, activeTab: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.activeTab, tabs[0]), theme: { activeThemeKey: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.themeInstalled, null), schemes, - activeScheme, + activeScheme }, - sort: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.sort, "stars"), + sort: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.sort, "stars") }; - if (!this.CONFIG.activeTab || !this.CONFIG.tabs.filter(tab => tab.name === this.CONFIG.activeTab).length) { + if (!this.CONFIG.activeTab || !this.CONFIG.tabs.filter((tab) => tab.name === this.CONFIG.activeTab).length) { this.CONFIG.activeTab = this.CONFIG.tabs[0].name; } } @@ -115,7 +120,7 @@ class App extends React.Component<{ this.CONFIG = { ...config }; console.debug("updated config", this.CONFIG); this.setState({ - CONFIG: { ...config }, + CONFIG: { ...config } }); }; @@ -130,10 +135,9 @@ class App extends React.Component<{ return null; } return ; - } // Otherwise, render the main Grid - else { - return ; } + + return ; } } diff --git a/src/components/Button.tsx b/src/components/Button.tsx index a26dc718..3a466e2e 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import type React from "react"; import styles from "../styles/modules/button.module.scss"; diff --git a/src/components/Card/AuthorsDiv.tsx b/src/components/Card/AuthorsDiv.tsx index 2df75386..b0e5077f 100644 --- a/src/components/Card/AuthorsDiv.tsx +++ b/src/components/Card/AuthorsDiv.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Author } from "../../types/marketplace-types"; +import type { Author } from "../../types/marketplace-types"; const AuthorsDiv = (props: { authors: Author[]; @@ -7,7 +7,7 @@ const AuthorsDiv = (props: { // Add a div with author links inside const authorsDiv = (
- { props.authors.map((author, index) => { + {props.authors.map((author) => { return ( e.stopPropagation()} - key={index} + key={author.name + author.url} > {author.name} diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 65ee9696..26071367 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -1,24 +1,17 @@ -import React, { Key } from "react"; -import { withTranslation } from "react-i18next"; import { t } from "i18next"; +import React, { type Key } from "react"; +import { withTranslation } from "react-i18next"; -import { CardItem, CardType, Config, SchemeIni, Snippet, VisualConfig } from "../../types/marketplace-types"; -import { LOCALSTORAGE_KEYS, CUSTOM_APP_PATH, SNIPPETS_PAGE_URL } from "../../constants"; -import { - getLocalStorageDataFromKey, - parseIni, - initializeSnippets, - parseCSS, - injectUserCSS, - generateKey, -} from "../../logic/Utils"; -import TrashIcon from "../Icons/TrashIcon"; +import { CUSTOM_APP_PATH, LOCALSTORAGE_KEYS, SNIPPETS_PAGE_URL } from "../../constants"; +import { openModal } from "../../logic/LaunchModals"; +import { generateKey, getLocalStorageDataFromKey, initializeSnippets, injectUserCSS, parseCSS, parseIni } from "../../logic/Utils"; +import type { CardItem, CardType, Config, SchemeIni, Snippet, VisualConfig } from "../../types/marketplace-types"; +import Button from "../Button"; import DownloadIcon from "../Icons/DownloadIcon"; import GitHubIcon from "../Icons/GitHubIcon"; -import { openModal } from "../../logic/LaunchModals"; +import TrashIcon from "../Icons/TrashIcon"; import AuthorsDiv from "./AuthorsDiv"; import TagsDiv from "./TagsDiv"; -import Button from "../Button"; const Spicetify = window.Spicetify; export type CardProps = { @@ -33,15 +26,18 @@ export type CardProps = { activeThemeKey?: string; }; -class Card extends React.Component { +export class Card extends React.Component< + CardProps, + { + installed: boolean; + // TODO: Can I remove `stars` from `this`? Or maybe just put everything in `state`? + stars: number; + tagsExpanded: boolean; + externalUrl: string; + lastUpdated: string | undefined; + created: string | undefined; + } +> { // Theme stuff // cssURL?: string; // schemesURL?: string; @@ -81,11 +77,12 @@ class Card extends React.Component res.json()); + const repoData = await fetch(url).then((res) => res.json()); const { stargazers_count, pushed_at } = repoData; const stateUpdate = { stars: 0, lastUpdated: undefined }; - if ((this.state.stars !== stargazers_count && this.props.CONFIG.visual.stars)) { + if (this.state.stars !== stargazers_count && this.props.CONFIG.visual.stars) { stateUpdate.stars = stargazers_count; console.debug(`Stars updated to: ${stargazers_count}`); } @@ -113,12 +110,12 @@ class Card extends React.Component { - if (this.props.type === "snippet") { - const processedName = this.props.item.title.replace(/\n/g, ""); - - if (getLocalStorageDataFromKey(`marketplace:installed:snippet:${processedName}`)?.custom) - return openModal("EDIT_SNIPPET", undefined, undefined, this.props); - - openModal("VIEW_SNIPPET", undefined, undefined, this.props, this.buttonClicked.bind(this)); - } else this.openReadme(); - } - }> +
{ + if (this.props.type === "snippet") { + const processedName = this.props.item.title.replace(/\n/g, ""); + + if (getLocalStorageDataFromKey(`marketplace:installed:snippet:${processedName}`)?.custom) + return openModal("EDIT_SNIPPET", undefined, undefined, this.props); + + openModal("VIEW_SNIPPET", undefined, undefined, this.props, this.buttonClicked.bind(this)); + } else this.openReadme(); + }} + >
@@ -475,7 +493,10 @@ class Card extends React.Component { // Set to transparent PNG to remove the placeholder icon // https://png-pixel.com - e.currentTarget.setAttribute("src", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII"); + e.currentTarget.setAttribute( + "src", + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII" + ); // Add class for styling e.currentTarget.closest(".main-cardImage-imageWrapper")?.classList.add("main-cardImage-imageWrapper--error"); @@ -490,17 +511,12 @@ class Card extends React.Component e.stopPropagation()} > -
- {this.props.item.title} -
+
{this.props.item.title}
{/* Add authors if they exist */} @@ -510,32 +526,29 @@ class Card extends React.Component {this.props.type === "snippet" ? this.props.item.description : this.props.item.manifest?.description}

- {this.props.item.lastUpdated && -

- {t("grid.lastUpdated", - { val: new Date(this.props.item.lastUpdated), + {this.props.item.lastUpdated && ( +

+ {t("grid.lastUpdated", { + val: new Date(this.props.item.lastUpdated), formatParams: { - val: { year: "numeric", month: "long", day: "numeric" }, - }, - }) - } -

} + val: { year: "numeric", month: "long", day: "numeric" } + } + })} +

+ )} {this.tags.length ? (
) : null} - {IS_INSTALLED && ( -
- ✓ {t("grid.installed")} -
- )} + {IS_INSTALLED &&
✓ {t("grid.installed")}
}
- - : null - } + > + ... + + ) : null}
); }; diff --git a/src/components/Grid.tsx b/src/components/Grid.tsx index f9e93c8b..5eba1fcd 100644 --- a/src/components/Grid.tsx +++ b/src/components/Grid.tsx @@ -1,53 +1,44 @@ import React from "react"; +import type { Option } from "react-dropdown"; import { withTranslation } from "react-i18next"; import semver from "semver"; -import { Option } from "react-dropdown"; const Spicetify = window.Spicetify; -import { CardItem, CardType, Config, SchemeIni, Snippet, TabItemConfig } from "../types/marketplace-types"; -import { getLocalStorageDataFromKey, - generateSchemesOptions, - injectColourScheme, - generateSortOptions, - sortCardItems, -} from "../logic/Utils"; -import { LOCALSTORAGE_KEYS, ITEMS_PER_REQUEST, MARKETPLACE_VERSION, LATEST_RELEASE_URL } from "../constants"; +import { ITEMS_PER_REQUEST, LATEST_RELEASE_URL, LOCALSTORAGE_KEYS, MARKETPLACE_VERSION } from "../constants"; +import { fetchAppManifest, fetchCssSnippets, fetchExtensionManifest, fetchThemeManifest, getBlacklist, getTaggedRepos } from "../logic/FetchRemotes"; import { openModal } from "../logic/LaunchModals"; -import { - getTaggedRepos, - fetchExtensionManifest, fetchThemeManifest, fetchAppManifest, - fetchCssSnippets, getBlacklist, -} from "../logic/FetchRemotes"; +import { generateSchemesOptions, generateSortOptions, getLocalStorageDataFromKey, injectColourScheme, sortCardItems } from "../logic/Utils"; +import type { CardItem, CardType, Config, SchemeIni, Snippet, TabItemConfig } from "../types/marketplace-types"; +import Button from "./Button"; +import Card, { type Card as CardClass } from "./Card/Card"; +import DownloadIcon from "./Icons/DownloadIcon"; import LoadMoreIcon from "./Icons/LoadMoreIcon"; import LoadingIcon from "./Icons/LoadingIcon"; import SettingsIcon from "./Icons/SettingsIcon"; import ThemeDeveloperToolsIcon from "./Icons/ThemeDeveloperToolsIcon"; import SortBox from "./Sortbox"; import { TopBarContent } from "./TabBar"; -import Card from "./Card/Card"; -import Button from "./Button"; -import DownloadIcon from "./Icons/DownloadIcon"; class Grid extends React.Component< -{ - title: string, - CONFIG: Config, - updateAppConfig: (CONFIG: Config) => void, - // TODO: there's probably a better way to make TS not complain about the withTranslation HOC - t: (key: string) => string, -}, -{ - version: string, - newUpdate: boolean, - searchValue: string, - cards: Card[], - tabs: TabItemConfig[], - rest: boolean, - endOfList: boolean, - activeThemeKey?: string, - schemes: SchemeIni, - activeScheme?: string | null, -} + { + title: string; + CONFIG: Config; + updateAppConfig: (CONFIG: Config) => void; + // TODO: there's probably a better way to make TS not complain about the withTranslation HOC + t: (key: string) => string; + }, + { + version: string; + newUpdate: boolean; + searchValue: string; + cards: CardClass[]; + tabs: TabItemConfig[]; + rest: boolean; + endOfList: boolean; + activeThemeKey?: string; + schemes: SchemeIni; + activeScheme?: string | null; + } > { constructor(props) { super(props); @@ -56,7 +47,7 @@ class Grid extends React.Component< // Fetches the sorting options, fetched from SortBox.js this.sortConfig = { - by: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.sort, "top"), + by: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.sort, "top") }; this.state = { @@ -69,7 +60,7 @@ class Grid extends React.Component< endOfList: false, schemes: props.CONFIG.theme.schemes, activeScheme: props.CONFIG.theme.activeScheme, - activeThemeKey: props.CONFIG.theme.activeThemeKey, + activeThemeKey: props.CONFIG.theme.activeThemeKey }; } @@ -78,7 +69,7 @@ class Grid extends React.Component< lastScroll = 0; requestQueue: never[][] = []; requestPage = 0; - cardList: Card[] = []; + cardList: CardClass[] = []; sortConfig: { by: string }; // TODO: why are these set up funny // To get to the other side @@ -116,21 +107,23 @@ class Grid extends React.Component< appendCard(item: CardItem | Snippet, type: CardType, activeTab: string) { if (activeTab !== this.props.CONFIG.activeTab) return; - const card = ; - - this.cardList.push(card as unknown as Card); + const card = ( + + ); + + this.cardList.push(card as unknown as CardClass); } // TODO: this isn't currently used, but it will be used for sorting (based on the SortBox component) @@ -146,7 +139,7 @@ class Grid extends React.Component< this.setState({ cards: [], rest: false, - endOfList: false, + endOfList: false }); this.endOfList = false; @@ -155,15 +148,14 @@ class Grid extends React.Component< updateTabs() { this.setState({ - tabs: [...this.props.CONFIG.tabs], + tabs: [...this.props.CONFIG.tabs] }); } updatePostsVisual() { this.cardList = this.cardList.map((card, index) => { - return ; - }) as unknown as Card[]; + return ; + }) as unknown as CardClass[]; this.setState({ cards: [...this.cardList] }); } @@ -176,7 +168,7 @@ class Grid extends React.Component< this.setState({ cards: [], rest: false, - endOfList: false, + endOfList: false }); this.endOfList = false; @@ -190,203 +182,190 @@ class Grid extends React.Component< // Store value for comparison later const activeTab = this.CONFIG.activeTab; switch (activeTab) { - case "Extensions": { - const pageOfRepos = await getTaggedRepos( - "spicetify-extensions", - this.requestPage, - this.BLACKLIST, - this.CONFIG.visual.showArchived, - ); - const extensions: CardItem[] = []; - for (const repo of pageOfRepos.items) { - const repoExtensions = await fetchExtensionManifest( - repo.contents_url, - repo.default_branch, - repo.stargazers_count, - this.CONFIG.visual.hideInstalled, - ); - - // I believe this stops the requests when switching tabs? - if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { - // Stop this queue from continuing to fetch and append to cards list - return -1; - } + case "Extensions": { + const pageOfRepos = await getTaggedRepos("spicetify-extensions", this.requestPage, this.BLACKLIST, this.CONFIG.visual.showArchived); + const extensions: CardItem[] = []; + for (const repo of pageOfRepos.items) { + const repoExtensions = await fetchExtensionManifest( + repo.contents_url, + repo.default_branch, + repo.stargazers_count, + this.CONFIG.visual.hideInstalled + ); + + // I believe this stops the requests when switching tabs? + if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { + // Stop this queue from continuing to fetch and append to cards list + return -1; + } - if (repoExtensions && repoExtensions.length) { - extensions.push(...repoExtensions.map((extension) => ({ - ...extension, - archived: repo.archived, - lastUpdated: repo.pushed_at, - created: repo.created_at, - }))); + if (repoExtensions?.length) { + extensions.push( + ...repoExtensions.map((extension) => ({ + ...extension, + archived: repo.archived, + lastUpdated: repo.pushed_at, + created: repo.created_at + })) + ); + } } - } - sortCardItems(extensions, localStorage.getItem("marketplace:sort") || "stars"); + sortCardItems(extensions, localStorage.getItem("marketplace:sort") || "stars"); + + for (const extension of extensions) { + this.appendCard(extension, "extension", activeTab); + } + this.setState({ cards: this.cardList }); - for (const extension of extensions) { - this.appendCard(extension, "extension", activeTab); + // First result is null or -1 so it coerces to 1 + const currentPage = this.requestPage > -1 && this.requestPage ? this.requestPage : 1; + // Sets the amount of items that have thus been fetched + const soFarResults = ITEMS_PER_REQUEST * (currentPage - 1) + pageOfRepos.page_count; + const remainingResults = pageOfRepos.total_count - soFarResults; + + // If still have more results, return next page number to fetch + console.debug(`Parsed ${soFarResults}/${pageOfRepos.total_count} extensions`); + if (remainingResults > 0) return currentPage + 1; + console.debug("No more extension results"); + break; } - this.setState({ cards: this.cardList }); - - // First result is null or -1 so it coerces to 1 - const currentPage = this.requestPage > -1 && this.requestPage ? this.requestPage : 1; - // Sets the amount of items that have thus been fetched - const soFarResults = ITEMS_PER_REQUEST * (currentPage - 1) + pageOfRepos.page_count; - const remainingResults = pageOfRepos.total_count - soFarResults; - - // If still have more results, return next page number to fetch - console.debug(`Parsed ${soFarResults}/${pageOfRepos.total_count} extensions`); - if (remainingResults > 0) return currentPage + 1; - else console.debug("No more extension results"); - break; - } case "Installed": { - const installedStuff = { - theme: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedThemes, []), - extension: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedExtensions, []), - snippet: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedSnippets, []), - }; - - for (const type in installedStuff) { - if (installedStuff[type].length) { - const installedOfType: CardItem[] = []; - installedStuff[type].forEach(async (itemKey) => { - // TODO: err handling - const installedItem = getLocalStorageDataFromKey(itemKey); - // I believe this stops the requests when switching tabs? - if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { - // Stop this queue from continuing to fetch and append to cards list - return -1; + case "Installed": { + const installedStuff = { + theme: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedThemes, []), + extension: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedExtensions, []), + snippet: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedSnippets, []) + }; + + for (const type in installedStuff) { + if (installedStuff[type].length) { + const installedOfType: CardItem[] = []; + for (const itemKey of installedStuff[type]) { + // TODO: err handling + const installedItem = getLocalStorageDataFromKey(itemKey); + // I believe this stops the requests when switching tabs? + if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { + // Stop this queue from continuing to fetch and append to cards list + return -1; + } + + installedOfType.push(installedItem); } - installedOfType.push(installedItem); - }); - - sortCardItems(installedOfType, localStorage.getItem("marketplace:sort") || "stars"); + sortCardItems(installedOfType, localStorage.getItem("marketplace:sort") || "stars"); - for (const item of installedOfType) { - this.appendCard(item, type as CardType, activeTab); + for (const item of installedOfType) { + this.appendCard(item, type as CardType, activeTab); + } } } + this.setState({ cards: this.cardList }); + break; + + // Don't need to return a page number because + // installed extension do them all in one go, since it's local } - this.setState({ cards: this.cardList }); - break; - - // Don't need to return a page number because - // installed extension do them all in one go, since it's local - } case "Themes": { - const pageOfRepos = await getTaggedRepos( - "spicetify-themes", - this.requestPage, - this.BLACKLIST, - this.CONFIG.visual.showArchived, - ); - const themes: CardItem[] = []; - for (const repo of pageOfRepos.items) { - const repoThemes = await fetchThemeManifest( - repo.contents_url, - repo.default_branch, - repo.stargazers_count, - ); - - // I believe this stops the requests when switching tabs? - if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { - // Stop this queue from continuing to fetch and append to cards list - return -1; + case "Themes": { + const pageOfRepos = await getTaggedRepos("spicetify-themes", this.requestPage, this.BLACKLIST, this.CONFIG.visual.showArchived); + const themes: CardItem[] = []; + for (const repo of pageOfRepos.items) { + const repoThemes = await fetchThemeManifest(repo.contents_url, repo.default_branch, repo.stargazers_count); + + // I believe this stops the requests when switching tabs? + if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { + // Stop this queue from continuing to fetch and append to cards list + return -1; + } + + if (repoThemes?.length) { + themes.push( + ...repoThemes.map((theme) => ({ + ...theme, + archived: repo.archived, + lastUpdated: repo.pushed_at, + created: repo.created_at + })) + ); + } } + this.setState({ cards: this.cardList }); - if (repoThemes && repoThemes.length) { - themes.push(...repoThemes.map( - (theme) => ({ - ...theme, - archived: repo.archived, - lastUpdated: repo.pushed_at, - created: repo.created_at, - }), - )); + sortCardItems(themes, localStorage.getItem("marketplace:sort") || "stars"); + + for (const theme of themes) { + this.appendCard(theme, "theme", activeTab); } - } - this.setState({ cards: this.cardList }); - sortCardItems(themes, localStorage.getItem("marketplace:sort") || "stars"); + // First request is null, so coerces to 1 + const currentPage = this.requestPage > -1 && this.requestPage ? this.requestPage : 1; + // -1 because the page number is 1-indexed + const soFarResults = ITEMS_PER_REQUEST * (currentPage - 1) + pageOfRepos.page_count; + const remainingResults = pageOfRepos.total_count - soFarResults; - for (const theme of themes) { - this.appendCard(theme, "theme", activeTab); + console.debug(`Parsed ${soFarResults}/${pageOfRepos.total_count} themes`); + if (remainingResults > 0) return currentPage + 1; + console.debug("No more theme results"); + break; } + case "Apps": { + const pageOfRepos = await getTaggedRepos("spicetify-apps", this.requestPage, this.BLACKLIST, this.CONFIG.visual.showArchived); + const apps: CardItem[] = []; + + for (const repo of pageOfRepos.items) { + const repoApps = await fetchAppManifest(repo.contents_url, repo.default_branch, repo.stargazers_count); + // I believe this stops the requests when switching tabs? + if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { + // Stop this queue from continuing to fetch and append to cards list + return -1; + } - // First request is null, so coerces to 1 - const currentPage = this.requestPage > -1 && this.requestPage ? this.requestPage : 1; - // -1 because the page number is 1-indexed - const soFarResults = ITEMS_PER_REQUEST * (currentPage - 1) + pageOfRepos.page_count; - const remainingResults = pageOfRepos.total_count - soFarResults; - - console.debug(`Parsed ${soFarResults}/${pageOfRepos.total_count} themes`); - if (remainingResults > 0) return currentPage + 1; - else console.debug("No more theme results"); - break; - } - case "Apps": { - const pageOfRepos = await getTaggedRepos( - "spicetify-apps", - this.requestPage, - this.BLACKLIST, - this.CONFIG.visual.showArchived, - ); - const apps: CardItem[] = []; - - for (const repo of pageOfRepos.items) { - const repoApps = await fetchAppManifest( - repo.contents_url, - repo.default_branch, - repo.stargazers_count, - ); - // I believe this stops the requests when switching tabs? - if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { - // Stop this queue from continuing to fetch and append to cards list - return -1; + if (repoApps?.length) { + apps.push( + ...repoApps.map((app) => ({ + ...app, + archived: repo.archived, + lastUpdated: repo.pushed_at, + created: repo.created_at + })) + ); + } } + this.setState({ cards: this.cardList }); - if (repoApps && repoApps.length) { - apps.push(...repoApps.map((app) => ({ - ...app, - archived: repo.archived, - lastUpdated: repo.pushed_at, - created: repo.created_at, - }))); + sortCardItems(apps, localStorage.getItem("marketplace:sort") || "stars"); + + for (const app of apps) { + this.appendCard(app, "app", activeTab); } - } - this.setState({ cards: this.cardList }); - sortCardItems(apps, localStorage.getItem("marketplace:sort") || "stars"); + // First request is null, so coerces to 1 + const currentPage = this.requestPage > -1 && this.requestPage ? this.requestPage : 1; + // -1 because the page number is 1-indexed + const soFarResults = ITEMS_PER_REQUEST * (currentPage - 1) + pageOfRepos.page_count; + const remainingResults = pageOfRepos.total_count - soFarResults; - for (const app of apps) { - this.appendCard(app, "app", activeTab); + console.debug(`Parsed ${soFarResults}/${pageOfRepos.total_count} apps`); + if (remainingResults > 0) return currentPage + 1; + console.debug("No more app results"); + break; } + case "Snippets": { + const snippets = this.SNIPPETS; - // First request is null, so coerces to 1 - const currentPage = this.requestPage > -1 && this.requestPage ? this.requestPage : 1; - // -1 because the page number is 1-indexed - const soFarResults = ITEMS_PER_REQUEST * (currentPage - 1) + pageOfRepos.page_count; - const remainingResults = pageOfRepos.total_count - soFarResults; - - console.debug(`Parsed ${soFarResults}/${pageOfRepos.total_count} apps`); - if (remainingResults > 0) return currentPage + 1; - else console.debug("No more app results"); - break; - } case "Snippets": { - const snippets = this.SNIPPETS; - - if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { - // Stop this queue from continuing to fetch and append to cards list - return -1; - } + if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) { + // Stop this queue from continuing to fetch and append to cards list + return -1; + } - if (snippets && snippets.length) { - sortCardItems(snippets, localStorage.getItem("marketplace:sort") || "stars"); - snippets.forEach((snippet) => this.appendCard(snippet, "snippet", activeTab)); - this.setState({ cards: this.cardList }); + if (snippets?.length) { + sortCardItems(snippets, localStorage.getItem("marketplace:sort") || "stars"); + for (const snippet of snippets) { + this.appendCard(snippet, "snippet", activeTab); + } + this.setState({ cards: this.cardList }); + } } - }} + } this.setState({ rest: true, endOfList: true }); this.endOfList = true; @@ -400,21 +379,16 @@ class Grid extends React.Component< */ async loadAmount(queue: never[], quantity: number = ITEMS_PER_REQUEST) { this.setState({ rest: false }); - quantity += this.cardList.length; + const maxCardQuantity = this.cardList.length + quantity; this.requestPage = await this.loadPage(queue); - while ( - this.requestPage && - this.requestPage !== -1 && - this.cardList.length < quantity && - !this.state.endOfList - ) { + while (this.requestPage && this.requestPage !== -1 && this.cardList.length < maxCardQuantity && !this.state.endOfList) { this.requestPage = await this.loadPage(queue); } if (this.requestPage === -1) { - this.requestQueue = this.requestQueue.filter(a => a !== queue); + this.requestQueue = this.requestQueue.filter((a) => a !== queue); return; } @@ -464,7 +438,7 @@ class Grid extends React.Component< this.setState({ schemes, - activeScheme, + activeScheme }); } @@ -477,23 +451,25 @@ class Grid extends React.Component< */ async componentDidMount() { // Checks for new Marketplace updates - fetch(LATEST_RELEASE_URL).then(res => res.json()).then( - result => { - if (result.message) throw result; - this.setState({ - version: result.name, - }); - - try { - this.setState({ newUpdate: semver.gt(result.name, MARKETPLACE_VERSION) }); - } catch (err) { - console.error(err); + fetch(LATEST_RELEASE_URL) + .then((res) => res.json()) + .then( + (result) => { + if (result.message) throw result; + this.setState({ + version: result.name + }); + + try { + this.setState({ newUpdate: semver.gt(result.name, MARKETPLACE_VERSION) }); + } catch (err) { + console.error(err); + } + }, + (error) => { + console.error("Failed to check for updates", error); } - }, - error => { - console.error("Failed to check for updates", error); - }, - ); + ); this.gridUpdateTabs = this.updateTabs.bind(this); this.gridUpdatePostsVisual = this.updatePostsVisual.bind(this); @@ -502,7 +478,8 @@ class Grid extends React.Component< this.checkScroll = this.isScrolledBottom.bind(this); if (viewPort) { viewPort.addEventListener("scroll", this.checkScroll); - if (this.cardList.length) { // Already loaded + if (this.cardList.length) { + // Already loaded if (this.lastScroll > 0) { viewPort.scrollTo(0, this.lastScroll); } @@ -536,7 +513,7 @@ class Grid extends React.Component< // Add scroll event listener with type isScrolledBottom(event: Event): void { const viewPort = event.target as HTMLElement; - if ((viewPort.scrollTop + viewPort.clientHeight) >= viewPort.scrollHeight) { + if (viewPort.scrollTop + viewPort.clientHeight >= viewPort.scrollHeight) { // At bottom, load more posts this.loadMore(); } @@ -558,39 +535,51 @@ class Grid extends React.Component<
- {this.state.newUpdate - ? - : null} + ) : null} {/* Generate a new box for sorting options */}

{t("grid.sort.label")}

this.updateSort(value)} sortBoxOptions={generateSortOptions(t)} - sortBySelectedFn={(a) => a.key === this.CONFIG.sort} /> + sortBySelectedFn={(a) => a.key === this.CONFIG.sort} + />
{/* Show theme developer tools button if themeDevTools is enabled */} - {this.CONFIG.visual.themeDevTools - ? - - : null} + ) : null} {/* Show colour scheme dropdown if there is a theme with schemes installed */} - {this.state.activeScheme ? this.updateColourSchemes(this.state.schemes, value)} - // TODO: Make this compatible with the changes to the theme install process: need to create a method to update the scheme options without a full reload. - sortBoxOptions={generateSchemesOptions(this.state.schemes)} - // It doesn't work when I directly use CONFIG.theme.activeScheme in the sortBySelectedFn - // because it hardcodes the value into the fn - sortBySelectedFn={(a) => a.key === this.getActiveScheme()} /> : null} + {this.state.activeScheme ? ( + this.updateColourSchemes(this.state.schemes, value)} + // TODO: Make this compatible with the changes to the theme install process: need to create a method to update the scheme options without a full reload. + sortBoxOptions={generateSchemesOptions(this.state.schemes)} + // It doesn't work when I directly use CONFIG.theme.activeScheme in the sortBySelectedFn + // because it hardcodes the value into the fn + sortBySelectedFn={(a) => a.key === this.getActiveScheme()} + /> + ) : null}
{ this.setState({ searchValue: event.target.value }); - }} /> + }} + />
- - : null} + {this.CONFIG.activeTab === "Snippets" ? ( + + ) : null}
- {!this.state.endOfList && (this.state.rest && this.state.cards.length > 0 ? : )} + {!this.state.endOfList && + (this.state.rest && this.state.cards.length > 0 ? : )}
- +
); } diff --git a/src/components/Icons/DownloadIcon.tsx b/src/components/Icons/DownloadIcon.tsx index d925b0ea..006c4a71 100644 --- a/src/components/Icons/DownloadIcon.tsx +++ b/src/components/Icons/DownloadIcon.tsx @@ -3,7 +3,10 @@ import React from "react"; const DownloadIcon = () => { return ( ); }; diff --git a/src/components/Icons/GitHubIcon.tsx b/src/components/Icons/GitHubIcon.tsx index be9b7986..584e55f4 100644 --- a/src/components/Icons/GitHubIcon.tsx +++ b/src/components/Icons/GitHubIcon.tsx @@ -3,8 +3,11 @@ import React from "react"; // Export GitHub icon SVG as a React component const GitHubIcon = () => { return ( - - + + ); }; diff --git a/src/components/Icons/LoadMoreIcon.tsx b/src/components/Icons/LoadMoreIcon.tsx index 736441bd..52cfc4be 100644 --- a/src/components/Icons/LoadMoreIcon.tsx +++ b/src/components/Icons/LoadMoreIcon.tsx @@ -1,18 +1,24 @@ import React from "react"; -export default class LoadMoreIcon extends React.Component< -{onClick: () => void} -> { +export default class LoadMoreIcon extends React.Component<{ onClick: () => void }> { render() { return (
-

»

- Load more +

+ » +

+ + Load more +
); } diff --git a/src/components/Icons/LoadingIcon.tsx b/src/components/Icons/LoadingIcon.tsx index 22c2d191..1a364392 100644 --- a/src/components/Icons/LoadingIcon.tsx +++ b/src/components/Icons/LoadingIcon.tsx @@ -18,14 +18,50 @@ const LoadingIcon = () => { // }))); return ( - + - - + + - - + + ); diff --git a/src/components/Icons/SettingsIcon.tsx b/src/components/Icons/SettingsIcon.tsx index 1a5f21b4..1550f695 100644 --- a/src/components/Icons/SettingsIcon.tsx +++ b/src/components/Icons/SettingsIcon.tsx @@ -3,7 +3,10 @@ import React from "react"; const SettingsIcon = () => { return ( ); }; diff --git a/src/components/Icons/ThemeDeveloperToolsIcon.tsx b/src/components/Icons/ThemeDeveloperToolsIcon.tsx index 0ef63642..1ecb6cff 100644 --- a/src/components/Icons/ThemeDeveloperToolsIcon.tsx +++ b/src/components/Icons/ThemeDeveloperToolsIcon.tsx @@ -2,9 +2,20 @@ import React from "react"; const ThemeDeveloperToolsIcon = () => { return ( - - - + + + + + + ); }; diff --git a/src/components/Icons/TooltipIcon.tsx b/src/components/Icons/TooltipIcon.tsx index 3813a963..b2f7eec1 100644 --- a/src/components/Icons/TooltipIcon.tsx +++ b/src/components/Icons/TooltipIcon.tsx @@ -2,7 +2,10 @@ import React from "react"; const TooltipIcon = () => { return ( - + + + + ); }; diff --git a/src/components/Icons/TrashIcon.tsx b/src/components/Icons/TrashIcon.tsx index e74ca1ac..fe21b89c 100644 --- a/src/components/Icons/TrashIcon.tsx +++ b/src/components/Icons/TrashIcon.tsx @@ -3,7 +3,10 @@ import React from "react"; const TrashIcon = () => { return ( ); }; diff --git a/src/components/Modals/BackupModal/index.tsx b/src/components/Modals/BackupModal/index.tsx index 66b74ab8..220cc0c2 100644 --- a/src/components/Modals/BackupModal/index.tsx +++ b/src/components/Modals/BackupModal/index.tsx @@ -1,13 +1,10 @@ -import React from "react"; import { t } from "i18next"; -import Editor from "react-simple-code-editor"; import { highlight, languages } from "prismjs/components/prism-core"; +import React from "react"; +import Editor from "react-simple-code-editor"; import "prismjs/components/prism-json"; -import { - exportMarketplace, - importMarketplace, -} from "../../../logic/Utils"; +import { exportMarketplace, importMarketplace } from "../../../logic/Utils"; import Button from "../../Button"; const BackupModal = () => { @@ -70,22 +67,24 @@ const BackupModal = () => {
setImportText(text)} - highlight={text => highlight(text, languages.css)} + onValueChange={(text) => setImportText(text)} + highlight={(text) => highlight(text, languages.css)} textareaId="marketplace-import-text" textareaClassName="import-textarea" readOnly={false} className="marketplace-code-editor-textarea" placeholder={t("backupModal.inputPlaceholder")} - style={{ - // fontFamily: "'Fira code', 'Fira Mono', monospace'", - // fontSize: 12, - }} + style={ + { + // fontFamily: "'Fira code', 'Fira Mono', monospace'", + // fontSize: 12, + } + } />
<> - -
diff --git a/src/components/Modals/Settings/ConfigRow.tsx b/src/components/Modals/Settings/ConfigRow.tsx index bee103e8..955ec9ac 100644 --- a/src/components/Modals/Settings/ConfigRow.tsx +++ b/src/components/Modals/Settings/ConfigRow.tsx @@ -1,8 +1,8 @@ import React from "react"; -import { Config } from "../../../types/marketplace-types"; -import Toggle from "../../Toggle"; -import SortBox from "../../Sortbox"; +import type { Config } from "../../../types/marketplace-types"; import TooltipIcon from "../../Icons/TooltipIcon"; +import SortBox from "../../Sortbox"; +import Toggle from "../../Toggle"; const Spicetify = window.Spicetify; const ConfigRow = (props: { @@ -16,9 +16,7 @@ const ConfigRow = (props: { description?: string | null; }) => { const type = props.type; - const componentId = (type === "dropdown") - ? "dropdown:" + props.storageKey - : "toggle:" + props.storageKey; + const componentId = type === "dropdown" ? `dropdown:${props.storageKey}` : `toggle:${props.storageKey}`; const enabled = !!props.modalConfig.visual[props.storageKey]; const settingsToggleChange = (e) => { @@ -46,25 +44,32 @@ const ConfigRow = (props: { if (type === "dropdown" && props.options) { return (
- +
{ return { key: option, - value: option, + value: option }; })} onChange={(value) => settingsDropdownChange(value)} sortBySelectedFn={(item) => { - return item.key == props.modalConfig.visual[props.storageKey]; + return item.key === props.modalConfig.visual[props.storageKey]; }} /> - {props.description.split("\n").map(line => { - return <>{line}
; + {props.description.split("\n").map((line) => { + return ( + + {line} +
+
+ ); })} } @@ -80,12 +85,13 @@ const ConfigRow = (props: {
- ); } return (
- +
diff --git a/src/components/Modals/Settings/TabRow.tsx b/src/components/Modals/Settings/TabRow.tsx index e0b8dd53..710c3434 100644 --- a/src/components/Modals/Settings/TabRow.tsx +++ b/src/components/Modals/Settings/TabRow.tsx @@ -1,13 +1,12 @@ -import React from "react"; import { t } from "i18next"; -import { Config } from "../../../types/marketplace-types"; +import React from "react"; import { LOCALSTORAGE_KEYS } from "../../../constants"; +import type { Config } from "../../../types/marketplace-types"; import Toggle from "../../Toggle"; const TabRow = (props: { name: string; - key?: number; // The React prop modalConfig: Config; updateConfig: (CONFIG: Config) => void; }) => { @@ -35,10 +34,7 @@ const TabRow = (props: { props.modalConfig.tabs[newPos] = props.modalConfig.tabs[currPos]; props.modalConfig.tabs[currPos] = temp; - localStorage.setItem( - LOCALSTORAGE_KEYS.tabs, - JSON.stringify(props.modalConfig.tabs), - ); + localStorage.setItem(LOCALSTORAGE_KEYS.tabs, JSON.stringify(props.modalConfig.tabs)); // Saves the config settings to app as well as SettingsModal state props.updateConfig(props.modalConfig); @@ -46,23 +42,29 @@ const TabRow = (props: { return (
- +
- +
); diff --git a/src/components/Modals/Settings/index.tsx b/src/components/Modals/Settings/index.tsx index 1cc7a542..edd7eda0 100644 --- a/src/components/Modals/Settings/index.tsx +++ b/src/components/Modals/Settings/index.tsx @@ -1,20 +1,20 @@ -import React from "react"; import { t } from "i18next"; +import React from "react"; -import { Config } from "../../../types/marketplace-types"; +import { LOCALSTORAGE_KEYS, MARKETPLACE_VERSION } from "../../../constants"; +import { openModal } from "../../../logic/LaunchModals"; import { getLocalStorageDataFromKey, resetMarketplace, sleep } from "../../../logic/Utils"; -import ConfigRow from "./ConfigRow"; +import type { Config } from "../../../types/marketplace-types"; import Button from "../../Button"; +import ConfigRow from "./ConfigRow"; import TabRow from "./TabRow"; -import { openModal } from "../../../logic/LaunchModals"; -import { LOCALSTORAGE_KEYS, MARKETPLACE_VERSION } from "../../../constants"; interface Props { CONFIG: Config; updateAppConfig: (CONFIG: Config) => void; } -const SettingsModal = ({ CONFIG, updateAppConfig } : Props) => { +const SettingsModal = ({ CONFIG, updateAppConfig }: Props) => { // Basically takes in the app's CONFIG and create the initial state, // and copies it into the SettingsModal state // then when updating anything in the main state, also updates the SettingsModal state @@ -48,27 +48,45 @@ const SettingsModal = ({ CONFIG, updateAppConfig } : Props) => { }; } - const AlbumArtColorDropDowns = getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.albumArtBasedColor) ? <> - - : null; + const AlbumArtColorDropDowns = getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.albumArtBasedColor) ? ( + <> + + + + ) : null; return (
-

{t("settings.optionsHeading")}

- - - - - - - + + + + + + + {AlbumArtColorDropDowns}

{t("settings.tabsHeading")}

- {modalConfig.tabs.map(({ name }, index) => { - return ; + {modalConfig.tabs.map(({ name }) => { + return ; })}
@@ -97,7 +115,6 @@ const SettingsModal = ({ CONFIG, updateAppConfig } : Props) => {
-
); }; @@ -116,7 +133,7 @@ const onBackupClick = async () => { // TODO: does it still work if I just attach to the settings modal itself? observer.observe(document.body, { childList: true, - subtree: true, + subtree: true }); Spicetify.PopupModal.hide(); diff --git a/src/components/Modals/Snippet/index.tsx b/src/components/Modals/Snippet/index.tsx index 141bbcf0..e8f98d53 100644 --- a/src/components/Modals/Snippet/index.tsx +++ b/src/components/Modals/Snippet/index.tsx @@ -1,20 +1,16 @@ -import React from "react"; import { t } from "i18next"; -import Editor from "react-simple-code-editor"; import { highlight, languages } from "prismjs/components/prism-core"; +import React from "react"; +import Editor from "react-simple-code-editor"; import "prismjs/components/prism-css"; -import { - getLocalStorageDataFromKey, - initializeSnippets, - fileToBase64, -} from "../../../logic/Utils"; import { LOCALSTORAGE_KEYS } from "../../../constants"; +import type { ModalType } from "../../../logic/LaunchModals"; +import { fileToBase64, getLocalStorageDataFromKey, initializeSnippets } from "../../../logic/Utils"; import Button from "../../Button"; -import { CardProps } from "../../Card/Card"; -import { ModalType } from "../../../logic/LaunchModals"; +import type { CardProps } from "../../Card/Card"; -const SnippetModal = (props: { content?: CardProps, type: ModalType, callback?: () => void }) => { +const SnippetModal = (props: { content?: CardProps; type: ModalType; callback?: () => void }) => { const PREVIEW_IMAGE_ID = "marketplace-customCSS-preview"; const [code, setCode] = React.useState(props.type === "ADD_SNIPPET" ? "" : props.content?.item.code || ""); const [name, setName] = React.useState(props.type === "ADD_SNIPPET" ? "" : props.content?.item.title || ""); @@ -44,7 +40,9 @@ const SnippetModal = (props: { content?: CardProps, type: ModalType, callback?: localStorage.removeItem(`marketplace:installed:snippet:${props.content.item.title}`); const installedSnippetKeys = getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedSnippets, []); - const remainingInstalledSnippetKeys = installedSnippetKeys.filter((key: string) => key !== `marketplace:installed:snippet:${props.content?.item.title}`); + const remainingInstalledSnippetKeys = installedSnippetKeys.filter( + (key: string) => key !== `marketplace:installed:snippet:${props.content?.item.title}` + ); localStorage.setItem(LOCALSTORAGE_KEYS.installedSnippets, JSON.stringify(remainingInstalledSnippetKeys)); } @@ -55,25 +53,17 @@ const SnippetModal = (props: { content?: CardProps, type: ModalType, callback?: code, description: processedDescription, imageURL, - custom: true, - }), + custom: true + }) ); // Add to installed list if not there already - const installedSnippetKeys = getLocalStorageDataFromKey( - LOCALSTORAGE_KEYS.installedSnippets, - [], - ); + const installedSnippetKeys = getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedSnippets, []); if (installedSnippetKeys.indexOf(localStorageKey) === -1) { installedSnippetKeys.push(localStorageKey); - localStorage.setItem( - LOCALSTORAGE_KEYS.installedSnippets, - JSON.stringify(installedSnippetKeys), - ); + localStorage.setItem(LOCALSTORAGE_KEYS.installedSnippets, JSON.stringify(installedSnippetKeys)); } - const installedSnippets = installedSnippetKeys.map((key: string) => - getLocalStorageDataFromKey(key), - ); + const installedSnippets = installedSnippetKeys.map((key: string) => getLocalStorageDataFromKey(key)); initializeSnippets(installedSnippets); Spicetify.PopupModal.hide(); @@ -92,89 +82,97 @@ const SnippetModal = (props: { content?: CardProps, type: ModalType, callback?:
setCode(code)} - highlight={code => highlight(code, languages.css)} + onValueChange={(code) => setCode(code)} + highlight={(code) => highlight(code, languages.css)} textareaId="marketplace-custom-css" textareaClassName="snippet-code-editor" readOnly={props.type === "VIEW_SNIPPET"} placeholder={t("snippets.customCSSPlaceholder")} - style={{ - // fontFamily: "'Fira code', 'Fira Mono', monospace'", - // fontSize: 12, - }} + style={ + { + // fontFamily: "'Fira code', 'Fira Mono', monospace'", + // fontSize: 12, + } + } />
- { - if (props.type !== "VIEW_SNIPPET") - setName(e.target.value); + { + if (props.type !== "VIEW_SNIPPET") setName(e.target.value); }} placeholder={t("snippets.snippetNamePlaceholder")} />
- - { - if (props.type !== "VIEW_SNIPPET") - setDescription(e.target.value); + + { + if (props.type !== "VIEW_SNIPPET") setDescription(e.target.value); }} placeholder={t("snippets.snippetDescPlaceholder")} />
- {imageURL && + {imageURL && ( - } + )}
- {props.type !== "VIEW_SNIPPET" + {props.type !== "VIEW_SNIPPET" && ( // Don't display buttons on "View Snippet" modal - && - <> - - {/* Disable the save button if the name or code are empty */} - - - } - { - props.type === "VIEW_SNIPPET" - && - - } + {/* Disable the save button if the name or code are empty */} + + + )} + {props.type === "VIEW_SNIPPET" && ( + + )}
); }; diff --git a/src/components/Modals/ThemeDevTools/index.tsx b/src/components/Modals/ThemeDevTools/index.tsx index f9450e97..853333d3 100644 --- a/src/components/Modals/ThemeDevTools/index.tsx +++ b/src/components/Modals/ThemeDevTools/index.tsx @@ -1,27 +1,21 @@ -import React from "react"; import { t } from "i18next"; -import Editor from "react-simple-code-editor"; import { highlight, languages } from "prismjs/components/prism-core"; +import React from "react"; +import Editor from "react-simple-code-editor"; import "prismjs/components/prism-ini"; -import Button from "../../Button"; -import { getInvalidCSS, getLocalStorageDataFromKey, unparseIni, parseIni } from "../../../logic/Utils"; import { LOCALSTORAGE_KEYS } from "../../../constants"; +import { getInvalidCSS, getLocalStorageDataFromKey, parseIni, unparseIni } from "../../../logic/Utils"; +import Button from "../../Button"; const themeKey = localStorage.getItem(LOCALSTORAGE_KEYS.themeInstalled); -const themeManifest = themeKey - ? getLocalStorageDataFromKey(themeKey) - : null; +const themeManifest = themeKey ? getLocalStorageDataFromKey(themeKey) : null; const ThemeDevToolsModal = () => { - const [code, setCode] = React.useState(themeManifest - ? unparseIni(themeManifest.schemes) - : t("devTools.noThemeInstalled"), - ); + const [code, setCode] = React.useState(themeManifest ? unparseIni(themeManifest.schemes) : t("devTools.noThemeInstalled")); return (
-
{/* Create a box containing the invalid css classnames fetched from "getInvalidCSS()"*/} @@ -52,10 +44,14 @@ const ThemeDevToolsModal = () => {
{getInvalidCSS().map((cssClass, index) => { - return
{cssClass}
; + return ( + // biome-ignore lint/suspicious/noArrayIndexKey: CSS classname is not unique +
+ {cssClass} +
+ ); })}
-
); diff --git a/src/components/Modals/Update/index.tsx b/src/components/Modals/Update/index.tsx index 738a79b4..9357fd38 100644 --- a/src/components/Modals/Update/index.tsx +++ b/src/components/Modals/Update/index.tsx @@ -1,11 +1,7 @@ -import React from "react"; import { t } from "i18next"; +import React from "react"; -import { - LATEST_RELEASE_URL, - MARKETPLACE_VERSION, - RELEASES_URL, -} from "../../../constants"; +import { LATEST_RELEASE_URL, MARKETPLACE_VERSION, RELEASES_URL } from "../../../constants"; import { getMarkdownHTML } from "../../../logic/Utils"; async function fetchLatestReleaseInfo(): Promise<{ @@ -18,13 +14,9 @@ async function fetchLatestReleaseInfo(): Promise<{ const { body, tag_name, message } = resultJson; return body && tag_name && !message ? { - version: tag_name.replace("v", ""), - changelog: await getMarkdownHTML( - body.match(/## What's Changed([\s\S]*?)(\r\n\r|\n\n##)/)[1], - "spicetify", - "spicetify-marketplace", - ), - } + version: tag_name.replace("v", ""), + changelog: await getMarkdownHTML(body.match(/## What's Changed([\s\S]*?)(\r\n\r|\n\n##)/)[1], "spicetify", "spicetify-marketplace") + } : null; } catch (error) { console.error(error); @@ -46,31 +38,21 @@ function UpdateModal(): React.ReactElement { ); diff --git a/src/components/ReadmePage.tsx b/src/components/ReadmePage.tsx index ef1c69be..a7c7f477 100644 --- a/src/components/ReadmePage.tsx +++ b/src/components/ReadmePage.tsx @@ -1,7 +1,7 @@ import React from "react"; import { withTranslation } from "react-i18next"; import { getMarkdownHTML } from "../logic/Utils"; -import { CardType } from "../types/marketplace-types"; +import type { CardType } from "../types/marketplace-types"; import Button from "./Button"; import DownloadIcon from "./Icons/DownloadIcon"; import GitHubIcon from "./Icons/GitHubIcon"; @@ -9,34 +9,34 @@ import LoadingIcon from "./Icons/LoadingIcon"; import TrashIcon from "./Icons/TrashIcon"; class ReadmePage extends React.Component< -{ - // props - // TODO: decide what data we want to pass in and how we want to store it - // (this currently comes from Card.openReadme) - data: { + { + // props + // TODO: decide what data we want to pass in and how we want to store it + // (this currently comes from Card.openReadme) + data: { + title: string; + user: string; + repo: string; + branch: string; + readmeURL: string; + readmeDir: string; + type: CardType; + install: () => void; + isInstalled: () => boolean; + }; title: string; - user: string; - repo: string; - branch: string; - readmeURL: string; - readmeDir: string; - type: CardType; - install: () => void; - isInstalled: () => boolean; + // TODO: there's probably a better way to make TS not complain about the withTranslation HOC + t: (key: string) => string; }, - title: string, - // TODO: there's probably a better way to make TS not complain about the withTranslation HOC - t: (key: string) => string, -}, -{ - isInstalled: boolean, - // state - html: string, -} + { + isInstalled: boolean; + // state + html: string; + } > { state = { isInstalled: this.props.data.isInstalled(), - html: `

${this.props.t("readmePage.loading")}

`, + html: `

${this.props.t("readmePage.loading")}

` }; getReadmeHTML = async () => { @@ -59,11 +59,10 @@ class ReadmePage extends React.Component< componentDidMount() { // Get and set readme html once loaded - this.getReadmeHTML() - .then((html) => { - if (html == null) return; - this.setState({ html }); - }); + this.getReadmeHTML().then((html) => { + if (html == null) return; + this.setState({ html }); + }); } componentDidUpdate() { @@ -86,15 +85,21 @@ class ReadmePage extends React.Component< // e.g. "screenshot.png" loads https://xpui.app.spotify.com/screenshot.png and breaks // so I turn it into https://raw.githubusercontent.com/theRealPadster/spicetify-hide-podcasts/main/screenshot.png // This works for urls relative to the repo readme + // biome-ignore lint/complexity/noForEach: querySelectorAll returns a NodeList, not an array document.querySelectorAll("#marketplace-readme img").forEach((img) => { - img.addEventListener("error", (e) => { - const element = e.target as HTMLImageElement; - const originalSrc = element.getAttribute("src"); - const fixedSrc = originalSrc?.charAt(0) === "/" - ? `https://raw.githubusercontent.com/${this.props.data.user}/${this.props.data.repo}/${this.props.data.branch}/${originalSrc?.slice(1)}` - : `${this.props.data.readmeURL.substring(0, this.props.data.readmeURL.lastIndexOf("/"))}/${originalSrc}`; - element.setAttribute("src", fixedSrc); - }, { once: true }); + img.addEventListener( + "error", + (e) => { + const element = e.target as HTMLImageElement; + const originalSrc = element.getAttribute("src"); + const fixedSrc = + originalSrc?.charAt(0) === "/" + ? `https://raw.githubusercontent.com/${this.props.data.user}/${this.props.data.repo}/${this.props.data.branch}/${originalSrc?.slice(1)}` + : `${this.props.data.readmeURL.substring(0, this.props.data.readmeURL.lastIndexOf("/"))}/${originalSrc}`; + element.setAttribute("src", fixedSrc); + }, + { once: true } + ); }); } @@ -102,29 +107,27 @@ class ReadmePage extends React.Component< if (this.props.data.type === "app") { return { icon: , - text: this.props.t("github"), + text: this.props.t("github") }; - } else if (this.state.isInstalled) { + } + if (this.state.isInstalled) { return { icon: , - text: this.props.t("remove"), - }; - } else { - return { - icon: , - text: this.props.t("install"), + text: this.props.t("remove") }; } + return { + icon: , + text: this.props.t("install") + }; } render() { - const expFeatures = JSON.parse( - localStorage.getItem("spicetify-exp-features") || "{}", - ); + const expFeatures = JSON.parse(localStorage.getItem("spicetify-exp-features") || "{}"); const isGlobalNav = expFeatures.enableGlobalNavBar?.value !== "control" && true; const tabBarMargin = { - marginTop: isGlobalNav ? "60px" : "0px", + marginTop: isGlobalNav ? "60px" : "0px" }; return ( @@ -143,15 +146,17 @@ class ReadmePage extends React.Component< }} label={this.buttonContent().text} > - {this.buttonContent().icon} - {" "} - {this.buttonContent().text} + {this.buttonContent().icon} {this.buttonContent().text} - {this.state.html === "

Loading...

" - ?
- :
} + {this.state.html === "

Loading...

" ? ( +
+ +
+ ) : ( +
+ )} ); } diff --git a/src/components/Sortbox.tsx b/src/components/Sortbox.tsx index ca460203..d950203b 100644 --- a/src/components/Sortbox.tsx +++ b/src/components/Sortbox.tsx @@ -1,6 +1,6 @@ import React from "react"; -import Dropdown, { Option } from "react-dropdown"; -import { SortBoxOption } from "../types/marketplace-types"; +import Dropdown, { type Option } from "react-dropdown"; +import type { SortBoxOption } from "../types/marketplace-types"; interface Props { sortBoxOptions: SortBoxOption[]; @@ -9,7 +9,6 @@ interface Props { } const SortBox = (props: Props) => { - const _onSelect = (item: Option) => { props.onChange(item.value); }; @@ -17,7 +16,7 @@ const SortBox = (props: Props) => { const options: Option[] = props.sortBoxOptions.map((item) => { return { value: item.key, - label: item.value, + label: item.value }; }); @@ -27,14 +26,9 @@ const SortBox = (props: Props) => { // Create a drop down menu
-
-
- - +
+
); diff --git a/src/components/TabBar.tsx b/src/components/TabBar.tsx index 8730a282..f54b50b9 100644 --- a/src/components/TabBar.tsx +++ b/src/components/TabBar.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; +import Dropdown, { type Option } from "react-dropdown"; import { withTranslation } from "react-i18next"; -import Dropdown, { Option } from "react-dropdown"; -import { TabItemConfig } from "../types/marketplace-types"; +import type { TabItemConfig } from "../types/marketplace-types"; // NOTE: The label and value are the same (e.g. "Extensions") type TabOptionConfig = Option & { @@ -13,12 +13,8 @@ class TabBarItem extends React.Component<{ item: TabOptionConfig; switchTo: (option: Option) => void; // TODO: there's probably a better way to make TS not complain about the withTranslation HOC - t: (key: string) => string, + t: (key: string) => string; }> { - constructor(props) { - super(props); - } - render() { const { t } = this.props; if (!this.props.item.enabled) return null; @@ -34,11 +30,9 @@ class TabBarItem extends React.Component<{ > {t(`tabs.${this.props.item.value}`)} @@ -53,25 +47,21 @@ interface TabBarMoreProps { items: TabOptionConfig[]; switchTo: (option: Option) => void; } -const TabBarMore = React.memo( - function TabBarMore({ items, switchTo } : TabBarMoreProps) { - return ( -
  • - -
  • - ); - }, -); +const TabBarMore = React.memo(function TabBarMore({ items, switchTo }: TabBarMoreProps) { + return ( +
  • + +
  • + ); +}); export const TopBarContent = (props: { links: TabItemConfig[]; activeLink: string; switchCallback: (option: Option) => void; }) => { - const resizeHost = document.querySelector(".Root__main-view .os-resize-observer-host") ?? document.querySelector(".Root__main-view .os-size-observer"); + const resizeHost = + document.querySelector(".Root__main-view .os-resize-observer-host") ?? document.querySelector(".Root__main-view .os-size-observer"); if (!resizeHost) return null; const [windowSize, setWindowSize] = useState(resizeHost.clientWidth); @@ -89,8 +79,8 @@ export const TopBarContent = (props: { document.querySelector(".main-topBar-container")?.setAttribute("style", "contain: unset;"); } Spicetify.Platform.History.listen(({ pathname }) => { - if (pathname != "/marketplace") { - // Delete tabBar from the dom + if (pathname !== "/marketplace") { + // Delete tabBar from the dom document.querySelector(".marketplace-tabBar")?.remove(); document.querySelector(".main-topBar-container")?.removeAttribute("style"); } @@ -103,18 +93,10 @@ export const TopBarContent = (props: { observer.disconnect(); }; }); - useEffect(()=>{ + useEffect(() => { contextHandler(); }); - return ( - - - ); + return ; }; interface TabBarProps { @@ -123,85 +105,78 @@ interface TabBarProps { switchCallback: (option: Option) => void; windowSize: number; } -const TabBar = React.memo( - function TabBar({ links, activeLink, switchCallback, windowSize = Infinity } : TabBarProps) { - const tabBarRef = React.useRef(null); - const [childrenSizes, setChildrenSizes] = useState([] as number[]); - const [availableSpace, setAvailableSpace] = useState(0); - const [droplistItem, setDroplistItems] = useState([] as number[]); - - // Key is the tab name, value is also the tab name, active is if it's active - const options = links.map(({ name, enabled }) => { - const active = name === activeLink; - return ({ label: name, value: name, active, enabled } as TabOptionConfig); - }); - - useEffect(() => { - if (!tabBarRef.current) return; - setAvailableSpace(tabBarRef.current.clientWidth); - }, [windowSize, tabBarRef.current?.clientWidth]); - - useEffect(() => { - if (!tabBarRef.current) return; +const TabBar = React.memo(function TabBar({ links, activeLink, switchCallback, windowSize = Number.POSITIVE_INFINITY }: TabBarProps) { + const tabBarRef = React.useRef(null); + const [childrenSizes, setChildrenSizes] = useState([] as number[]); + const [availableSpace, setAvailableSpace] = useState(0); + const [droplistItem, setDroplistItems] = useState([] as number[]); + + // Key is the tab name, value is also the tab name, active is if it's active + const options = links.map(({ name, enabled }) => { + const active = name === activeLink; + return { label: name, value: name, active, enabled } as TabOptionConfig; + }); - const children = Array.from(tabBarRef.current.children); - const tabbarItemSizes = children.map(child => child.clientWidth); + // biome-ignore lint/correctness/useExhaustiveDependencies: Run when windowSize changes + useEffect(() => { + if (!tabBarRef.current) return; + setAvailableSpace(tabBarRef.current.clientWidth); + }, [windowSize, tabBarRef.current?.clientWidth]); - setChildrenSizes(tabbarItemSizes); - }, [links]); + // biome-ignore lint/correctness/useExhaustiveDependencies: Run when links change + useEffect(() => { + if (!tabBarRef.current) return; - useEffect(() => { - if (!tabBarRef.current) return; + const children = Array.from(tabBarRef.current.children); + const tabbarItemSizes = children.map((child) => child.clientWidth); - const totalSize = childrenSizes.reduce((a, b) => a + b, 0); + setChildrenSizes(tabbarItemSizes); + }, [links]); - // Can we render everything? - if (totalSize <= availableSpace) { - setDroplistItems([]); - return; - } + useEffect(() => { + if (!tabBarRef.current) return; - // The `More` button can be set to _any_ of the children. So we - // reserve space for the largest item instead of always taking - // the last item. - const viewMoreButtonSize = Math.max(...childrenSizes); + const totalSize = childrenSizes.reduce((a, b) => a + b, 0); - // Figure out how many children we can render while also showing - // the More button - const itemsToHide = [] as number[]; - let stopWidth = viewMoreButtonSize; + // Can we render everything? + if (totalSize <= availableSpace) { + setDroplistItems([]); + return; + } - childrenSizes.forEach((childWidth, i) => { - if (availableSpace >= stopWidth + childWidth) { - stopWidth += childWidth; - } else { - itemsToHide.push(i); - } - }); + // The `More` button can be set to _any_ of the children. So we + // reserve space for the largest item instead of always taking + // the last item. + const viewMoreButtonSize = Math.max(...childrenSizes); + + // Figure out how many children we can render while also showing + // the More button + const itemsToHide = [] as number[]; + let stopWidth = viewMoreButtonSize; + + childrenSizes.forEach((childWidth, i) => { + if (availableSpace >= stopWidth + childWidth) { + stopWidth += childWidth; + } else { + itemsToHide.push(i); + } + }); - setDroplistItems(itemsToHide); - }, [availableSpace, childrenSizes]); + setDroplistItems(itemsToHide); + }, [availableSpace, childrenSizes]); - return ( - - ); - }, -); + return ( + + ); +}); diff --git a/src/components/Toggle.tsx b/src/components/Toggle.tsx index eb4f5610..af055aae 100644 --- a/src/components/Toggle.tsx +++ b/src/components/Toggle.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import type React from "react"; import styles from "../styles/modules/toggle.module.scss"; @@ -16,14 +16,17 @@ const Toggle = (props: { return ( ); diff --git a/src/constants.ts b/src/constants.ts index 41c63b49..5f955975 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,5 @@ -import { TabItemConfig } from "./types/marketplace-types"; import { version } from "../package.json"; +import type { TabItemConfig } from "./types/marketplace-types"; export const MARKETPLACE_VERSION = version; @@ -17,7 +17,7 @@ export const LOCALSTORAGE_KEYS = { albumArtBasedColor: `${STORAGE_KEY_PREFIX}:albumArtBasedColors`, albumArtBasedColorMode: `${STORAGE_KEY_PREFIX}:albumArtBasedColorsMode`, albumArtBasedColorVibrancy: `${STORAGE_KEY_PREFIX}:albumArtBasedColorsVibrancy`, - colorShift: `${STORAGE_KEY_PREFIX}:colorShift`, + colorShift: `${STORAGE_KEY_PREFIX}:colorShift` }; // Initalize topbar tabs @@ -27,7 +27,7 @@ export const ALL_TABS: TabItemConfig[] = [ { name: "Themes", enabled: true }, { name: "Snippets", enabled: true }, { name: "Apps", enabled: true }, - { name: "Installed", enabled: true }, + { name: "Installed", enabled: true } ]; // Max GitHub API items per page @@ -39,14 +39,11 @@ export const CUSTOM_APP_PATH = "/marketplace"; // Used in Card.tsx export const MAX_TAGS = 4; -export const SNIPPETS_PAGE_URL = - "https://github.com/spicetify/spicetify-marketplace/blob/main/src/resources/snippets.ts"; +export const SNIPPETS_PAGE_URL = "https://github.com/spicetify/spicetify-marketplace/blob/main/src/resources/snippets.ts"; -export const SNIPPETS_URL = - "https://raw.githubusercontent.com/spicetify/spicetify-marketplace/main/resources/snippets.json"; +export const SNIPPETS_URL = "https://raw.githubusercontent.com/spicetify/spicetify-marketplace/main/resources/snippets.json"; -export const BLACKLIST_URL = - "https://raw.githubusercontent.com/spicetify/spicetify-marketplace/main/resources/blacklist.json"; +export const BLACKLIST_URL = "https://raw.githubusercontent.com/spicetify/spicetify-marketplace/main/resources/blacklist.json"; export const RELEASES_URL = "https://github.com/spicetify/spicetify-marketplace/releases"; diff --git a/src/extensions/extension.tsx b/src/extensions/extension.tsx index e28c45c0..a5bc720c 100644 --- a/src/extensions/extension.tsx +++ b/src/extensions/extension.tsx @@ -5,29 +5,24 @@ import { t } from "i18next"; import { ITEMS_PER_REQUEST, LOCALSTORAGE_KEYS, MARKETPLACE_VERSION } from "../constants"; -import { RepoType } from "../types/marketplace-types"; +import { fetchAppManifest, fetchExtensionManifest, fetchThemeManifest, getBlacklist } from "../logic/FetchRemotes"; import { - getLocalStorageDataFromKey, - resetMarketplace, + addExtensionToSpicetifyConfig, exportMarketplace, - isGithubRawUrl, + getAvailableTLD, + getLocalStorageDataFromKey, getParamsFromGithubRaw, + initAlbumArtBasedColor, + initColorShiftLoop, initializeSnippets, injectColourScheme, - initColorShiftLoop, - parseCSS, // TODO: there's a slightly different copy of this function in Card.ts? injectUserCSS, - addExtensionToSpicetifyConfig, - initAlbumArtBasedColor, - getAvailableTLD, + isGithubRawUrl, + parseCSS, + resetMarketplace } from "../logic/Utils"; -import { - getBlacklist, - fetchThemeManifest, - fetchExtensionManifest, - fetchAppManifest, -} from "../logic/FetchRemotes"; +import type { RepoType } from "../types/marketplace-types"; (async function init() { if (!Spicetify.LocalStorage || !Spicetify.showNotification) { @@ -49,7 +44,7 @@ import { reset: resetMarketplace, // Export all marketplace localstorage keys export: exportMarketplace, - version: MARKETPLACE_VERSION, + version: MARKETPLACE_VERSION }; const tld = await getAvailableTLD(); @@ -126,10 +121,10 @@ import { Spicetify.Config.current_theme = themeManifest.manifest?.name; // Inject any included js - if (themeManifest.include && themeManifest.include.length) { + if (themeManifest.include?.length) { // console.log("Including js", installedThemeData.include); - themeManifest.include.forEach((script) => { + for (const script of themeManifest.include) { const newScript = document.createElement("script"); let src = script; @@ -147,7 +142,7 @@ import { // Add to Spicetify.Config addExtensionToSpicetifyConfig(script); - }); + } } }; @@ -172,7 +167,9 @@ import { window.sessionStorage.setItem("marketplace-request-tld", tld); const installedExtensions = getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedExtensions, []); - installedExtensions.forEach((extensionKey) => initializeExtension(extensionKey)); + for (const extensionKey of installedExtensions) { + initializeExtension(extensionKey); + } const { current_theme: localTheme } = Spicetify.Config; localStorage.setItem(LOCALSTORAGE_KEYS.localTheme, localTheme); @@ -198,9 +195,11 @@ async function queryRepos(type: RepoType, pageNum = 1) { let url = `https://api.github.com/search/repositories?per_page=${ITEMS_PER_REQUEST}&q=${encodeURIComponent(`topic:spicetify-${type}s`)}`; if (pageNum) url += `&page=${pageNum}`; - const allRepos = JSON.parse(window.sessionStorage.getItem(`spicetify-${type}s-page-${pageNum}`) || "null") || await fetch(url) - .then(res => res.json()) - .catch(() => null); + const allRepos = + JSON.parse(window.sessionStorage.getItem(`spicetify-${type}s-page-${pageNum}`) || "null") || + (await fetch(url) + .then((res) => res.json()) + .catch(() => null)); if (!allRepos?.items) { Spicetify.showNotification(t("notifications.tooManyRequests"), true, 5000); @@ -212,7 +211,7 @@ async function queryRepos(type: RepoType, pageNum = 1) { const filteredResults = { ...allRepos, page_count: allRepos.items.length, - items: allRepos.items.filter(item => !BLACKLIST?.includes(item.html_url)), + items: allRepos.items.filter((item) => !BLACKLIST?.includes(item.html_url)) }; return filteredResults; @@ -235,8 +234,8 @@ async function loadPageRecursive(type: RepoType, pageNum: number) { // If still have more results, recursively fetch next page console.debug(`Parsed ${soFarResults}/${pageOfRepos.total_count} ${type}s`); - if (remainingResults > 0) return await loadPageRecursive(type, pageNum + 1); // There are more results. currentPage + 1 is the next page to fetch. - else console.debug(`No more ${type} results`); + if (remainingResults > 0) return await loadPageRecursive(type, pageNum + 1); + console.debug(`No more ${type} results`); } (async function initializePreload() { @@ -250,11 +249,7 @@ async function loadPageRecursive(type: RepoType, pageNum: number) { // Begin by getting the themes and extensions from github // const [extensionReposArray, themeReposArray] = await Promise.all([ - await Promise.all([ - loadPageRecursive("extension", 0), - loadPageRecursive("theme", 0), - loadPageRecursive("app", 0), - ]); + await Promise.all([loadPageRecursive("extension", 0), loadPageRecursive("theme", 0), loadPageRecursive("app", 0)]); // let extensionsNextPage = 1; // let themesNextPage = 1; diff --git a/src/logic/FetchRemotes.ts b/src/logic/FetchRemotes.ts index bbcbbf26..1409fded 100644 --- a/src/logic/FetchRemotes.ts +++ b/src/logic/FetchRemotes.ts @@ -1,7 +1,7 @@ import { t } from "i18next"; -import { BLACKLIST_URL, SNIPPETS_URL, ITEMS_PER_REQUEST } from "../constants"; -import { RepoTopic, CardItem, Snippet } from "../types/marketplace-types"; +import { BLACKLIST_URL, ITEMS_PER_REQUEST, SNIPPETS_URL } from "../constants"; +import type { CardItem, RepoTopic, Snippet } from "../types/marketplace-types"; import { addToSessionStorage, processAuthors } from "./Utils"; // TODO: add sort type, order, etc? @@ -14,7 +14,7 @@ import { addToSessionStorage, processAuthors } from "./Utils"; * @param page The query page number * @returns Array of search results (filtered through the blacklist) */ -export async function getTaggedRepos(tag: RepoTopic, page = 1, BLACKLIST:string[] = [], showArchived = false) { +export async function getTaggedRepos(tag: RepoTopic, page = 1, BLACKLIST: string[] = [], showArchived = false) { // www is needed or it will block with "cross-origin" error. let url = `https://api.github.com/search/repositories?q=${encodeURIComponent(`topic:${tag}`)}&per_page=${ITEMS_PER_REQUEST}`; @@ -24,9 +24,11 @@ export async function getTaggedRepos(tag: RepoTopic, page = 1, BLACKLIST:string[ // Sorting params (not implemented for Marketplace yet) // if (sortConfig.by.match(/top|controversial/) && sortConfig.time) { // url += `&t=${sortConfig.time}` - const allRepos = JSON.parse(window.sessionStorage.getItem(`${tag}-page-${page}`) || "null") || await fetch(url) - .then(res => res.json()) - .catch(() => null); + const allRepos = + JSON.parse(window.sessionStorage.getItem(`${tag}-page-${page}`) || "null") || + (await fetch(url) + .then((res) => res.json()) + .catch(() => null)); if (!allRepos?.items) { Spicetify.showNotification(t("notifications.tooManyRequests"), true, 5000); @@ -40,7 +42,7 @@ export async function getTaggedRepos(tag: RepoTopic, page = 1, BLACKLIST:string[ // Include count of all items on the page, since we're filtering the blacklist below, // which can mess up the paging logic page_count: allRepos.items.length, - items: allRepos.items.filter(item => !BLACKLIST.includes(item.html_url) && (showArchived || !item.archived)), + items: allRepos.items.filter((item) => !BLACKLIST.includes(item.html_url) && (showArchived || !item.archived)) }; return filteredResults; @@ -75,12 +77,12 @@ async function fetchRepoManifest(url: string) { // TODO: add try/catch here? // TODO: can we add a return type here? /** -* Get the manifest object for a repo -* @param user Owner username -* @param repo Repo name -* @param branch Default branch name (e.g. main or master) -* @returns The manifest object -*/ + * Get the manifest object for a repo + * @param user Owner username + * @param repo Repo name + * @param branch Default branch name (e.g. main or master) + * @returns The manifest object + */ async function getRepoManifest(user: string, repo: string, branch: string) { const key = `${user}-${repo}`; const sessionStorageItem = window.sessionStorage.getItem(key); @@ -102,13 +104,13 @@ async function getRepoManifest(user: string, repo: string, branch: string) { // TODO: can we add a return type here? /** -* Fetch extensions from a repo and format data for generating cards -* @param contents_url The repo's GitHub API contents_url (e.g. "https://api.github.com/repos/theRealPadster/spicetify-hide-podcasts/contents/{+path}") -* @param branch The repo's default branch (e.g. main or master) -* @param stars The number of stars the repo has -* @param hideInstalled Whether to hide installed items or not (defaults to `false`) -* @returns Extension info for card (or null) -*/ + * Fetch extensions from a repo and format data for generating cards + * @param contents_url The repo's GitHub API contents_url (e.g. "https://api.github.com/repos/theRealPadster/spicetify-hide-podcasts/contents/{+path}") + * @param branch The repo's default branch (e.g. main or master) + * @param stars The number of stars the repo has + * @param hideInstalled Whether to hide installed items or not (defaults to `false`) + * @returns Extension info for card (or null) + */ export async function fetchExtensionManifest(contents_url: string, branch: string, stars: number, hideInstalled = false) { try { // TODO: use the original search full_name ("theRealPadster/spicetify-hide-podcasts") or something to get the url better? @@ -131,29 +133,23 @@ export async function fetchExtensionManifest(contents_url: string, branch: strin repo, branch: selectedBranch, - imageURL: manifest.preview && manifest.preview.startsWith("http") + imageURL: manifest.preview?.startsWith("http") ? manifest.preview : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.preview}`, extensionURL: manifest.main.startsWith("http") ? manifest.main : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.main}`, - readmeURL: manifest.readme && manifest.readme.startsWith("http") + readmeURL: manifest.readme?.startsWith("http") ? manifest.readme : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.readme}`, stars, - tags: manifest.tags, + tags: manifest.tags }; // If manifest is valid, add it to the list - if (manifest && manifest.name && manifest.description && manifest.main - ) { + if (manifest?.name && manifest.description && manifest.main) { // Add to list unless we're hiding installed items and it's installed - if ( - !( - hideInstalled && - localStorage.getItem("marketplace:installed:" + `${user}/${repo}/${manifest.main}`) - ) - ) { + if (!(hideInstalled && localStorage.getItem(`marketplace:installed:${user}/${repo}/${manifest.main}`))) { accum.push(item); } } @@ -172,12 +168,12 @@ export async function fetchExtensionManifest(contents_url: string, branch: strin // TODO: can we add a return type here? /** -* Fetch themes from a repo and format data for generating cards -* @param contents_url The repo's GitHub API contents_url (e.g. "https://api.github.com/repos/theRealPadster/spicetify-hide-podcasts/contents/{+path}") -* @param branch The repo's default branch (e.g. main or master) -* @param stars The number of stars the repo has -* @returns Extension info for card (or null) -*/ + * Fetch themes from a repo and format data for generating cards + * @param contents_url The repo's GitHub API contents_url (e.g. "https://api.github.com/repos/theRealPadster/spicetify-hide-podcasts/contents/{+path}") + * @param branch The repo's default branch (e.g. main or master) + * @param stars The number of stars the repo has + * @returns Extension info for card (or null) + */ export async function fetchThemeManifest(contents_url: string, branch: string, stars: number) { try { const regex_result = contents_url.match(/https:\/\/api\.github\.com\/repos\/(?.+)\/(?.+)\/contents/); @@ -199,10 +195,10 @@ export async function fetchThemeManifest(contents_url: string, branch: string, s user, repo, branch: selectedBranch, - imageURL: manifest.preview && manifest.preview.startsWith("http") + imageURL: manifest.preview?.startsWith("http") ? manifest.preview : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.preview}`, - readmeURL: manifest.readme && manifest.readme.startsWith("http") + readmeURL: manifest.readme?.startsWith("http") ? manifest.readme : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.readme}`, stars, @@ -213,11 +209,11 @@ export async function fetchThemeManifest(contents_url: string, branch: string, s : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.usercss}`, // TODO: clean up indentation etc schemesURL: manifest.schemes - ? ( - manifest.schemes.startsWith("http") ? manifest.schemes : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.schemes}` - ) + ? manifest.schemes.startsWith("http") + ? manifest.schemes + : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.schemes}` : null, - include: manifest.include, + include: manifest.include }; // If manifest is valid, add it to the list if (manifest?.name && manifest?.usercss && manifest?.description) { @@ -232,12 +228,12 @@ export async function fetchThemeManifest(contents_url: string, branch: string, s } /** -* Fetch custom apps from a repo and format data for generating cards -* @param contents_url The repo's GitHub API contents_url (e.g. "https://api.github.com/repos/theRealPadster/spicetify-hide-podcasts/contents/{+path}") -* @param branch The repo's default branch (e.g. main or master) -* @param stars The number of stars the repo has -* @returns Extension info for card (or null) -*/ + * Fetch custom apps from a repo and format data for generating cards + * @param contents_url The repo's GitHub API contents_url (e.g. "https://api.github.com/repos/theRealPadster/spicetify-hide-podcasts/contents/{+path}") + * @param branch The repo's default branch (e.g. main or master) + * @param stars The number of stars the repo has + * @returns Extension info for card (or null) + */ export async function fetchAppManifest(contents_url: string, branch: string, stars: number) { try { // TODO: use the original search full_name ("theRealPadster/spicetify-hide-podcasts") or something to get the url better? @@ -261,22 +257,22 @@ export async function fetchAppManifest(contents_url: string, branch: string, sta repo, branch: selectedBranch, - imageURL: manifest.preview && manifest.preview.startsWith("http") + imageURL: manifest.preview?.startsWith("http") ? manifest.preview : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.preview}`, // Custom Apps don't have an entry point; they're just listed so they can link out from the card // extensionURL: manifest.main.startsWith("http") // ? manifest.main // : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.main}`, - readmeURL: manifest.readme && manifest.readme.startsWith("http") + readmeURL: manifest.readme?.startsWith("http") ? manifest.readme : `https://raw.githubusercontent.com/${user}/${repo}/${selectedBranch}/${manifest.readme}`, stars, - tags: manifest.tags, + tags: manifest.tags }; // If manifest is valid, add it to the list - if (manifest && manifest.name && manifest.description) { + if (manifest?.name && manifest.description) { accum.push(item); } // else { @@ -293,20 +289,24 @@ export async function fetchAppManifest(contents_url: string, branch: string, sta } /** -* It fetches the blacklist.json file from the GitHub repository and returns the array of blocked repos. -* @returns String array of blacklisted repos -*/ + * It fetches the blacklist.json file from the GitHub repository and returns the array of blocked repos. + * @returns String array of blacklisted repos + */ export const getBlacklist = async () => { - const json = await fetch(BLACKLIST_URL).then(res => res.json()).catch(() => ({})); + const json = await fetch(BLACKLIST_URL) + .then((res) => res.json()) + .catch(() => ({})); return json.repos as string[] | undefined; }; /** -* It fetches the snippets.json file from the Github repository and returns it as an array of snippets. -* @returns Array of snippets -*/ + * It fetches the snippets.json file from the Github repository and returns it as an array of snippets. + * @returns Array of snippets + */ export const fetchCssSnippets = async () => { - const snippetsJSON = await fetch(SNIPPETS_URL).then(res => res.json()).catch(() => []) as Snippet[]; + const snippetsJSON = (await fetch(SNIPPETS_URL) + .then((res) => res.json()) + .catch(() => [])) as Snippet[]; if (!snippetsJSON.length) return []; const snippets = snippetsJSON.reduce((accum, snippet) => { @@ -317,7 +317,7 @@ export const fetchCssSnippets = async () => { snip.imageURL = snip.preview.startsWith("http") ? snip.preview : `https://raw.githubusercontent.com/spicetify/spicetify-marketplace/main/${snip.preview}`; - delete snip.preview; + snip.preview = undefined; } accum.push(snip); @@ -325,4 +325,3 @@ export const fetchCssSnippets = async () => { }, []); return snippets; }; - diff --git a/src/logic/LaunchModals.tsx b/src/logic/LaunchModals.tsx index c262bc98..b3da1046 100644 --- a/src/logic/LaunchModals.tsx +++ b/src/logic/LaunchModals.tsx @@ -1,14 +1,14 @@ -import React from "react"; import { t } from "i18next"; -import { Config } from "../types/marketplace-types"; +import React from "react"; +import type { Config } from "../types/marketplace-types"; -import SnippetModal from "../components/Modals/Snippet"; +import type { CardProps } from "../components/Card/Card"; +import BackupModal from "../components/Modals/BackupModal"; import ReloadModal from "../components/Modals/Reload"; import SettingsModal from "../components/Modals/Settings"; +import SnippetModal from "../components/Modals/Snippet"; import ThemeDevToolsModal from "../components/Modals/ThemeDevTools"; -import BackupModal from "../components/Modals/BackupModal"; import UpdateModal from "../components/Modals/Update"; -import { CardProps } from "../components/Card/Card"; export type ModalType = "ADD_SNIPPET" | "EDIT_SNIPPET" | "VIEW_SNIPPET" | "RELOAD" | "SETTINGS" | "THEME_DEV_TOOLS" | "BACKUP" | "UPDATE"; @@ -17,65 +17,65 @@ const getModalSettings = ( CONFIG?: Config, updateAppConfig?: (CONFIG: Config) => void, props?: CardProps, - callback?: () => void, + callback?: () => void ) => { switch (modalType) { - case "ADD_SNIPPET": - return { - title: t("snippets.addTitle"), - content: , - isLarge: true, - }; - case "EDIT_SNIPPET": - return { - title: t("snippets.editTitle"), - content: , - isLarge: true, - }; - case "VIEW_SNIPPET": - return { - title: t("snippets.viewTitle"), - content: , - isLarge: true, - }; - case "RELOAD": - return { - title: t("reloadModal.title"), - content: , - isLarge: false, - }; - case "SETTINGS": - return { - title: t("settings.title"), - // TODO: If I just use {CONFIG}, it nests it inside another object... - content: void} />, - isLarge: true, - }; - case "THEME_DEV_TOOLS": - return { - title: t("devTools.title"), - content: , - isLarge: true, - }; - case "BACKUP": - return { - title: t("backupModal.title"), - content: , - isLarge: true, - }; - case "UPDATE": - return { - title: t("updateModal.title"), - content: , - isLarge: true, - }; + case "ADD_SNIPPET": + return { + title: t("snippets.addTitle"), + content: , + isLarge: true + }; + case "EDIT_SNIPPET": + return { + title: t("snippets.editTitle"), + content: , + isLarge: true + }; + case "VIEW_SNIPPET": + return { + title: t("snippets.viewTitle"), + content: , + isLarge: true + }; + case "RELOAD": + return { + title: t("reloadModal.title"), + content: , + isLarge: false + }; + case "SETTINGS": + return { + title: t("settings.title"), + // TODO: If I just use {CONFIG}, it nests it inside another object... + content: void} />, + isLarge: true + }; + case "THEME_DEV_TOOLS": + return { + title: t("devTools.title"), + content: , + isLarge: true + }; + case "BACKUP": + return { + title: t("backupModal.title"), + content: , + isLarge: true + }; + case "UPDATE": + return { + title: t("updateModal.title"), + content: , + isLarge: true + }; - default: - return { - title: "", - content:
    , - isLarge: false, - }; + default: + return { + title: "", + content:
    , + isLarge: false + }; } }; @@ -84,7 +84,7 @@ export const openModal = ( CONFIG?: Config, updateAppConfig?: (CONFIG: Config) => void, props?: CardProps, - callback?: () => void, + callback?: () => void ) => { const triggerModal = () => { const modalSettings = getModalSettings(modal, CONFIG, updateAppConfig, props, callback); @@ -94,4 +94,3 @@ export const openModal = ( triggerModal(); return; }; - diff --git a/src/logic/Utils.ts b/src/logic/Utils.ts index 38fa9895..f583f969 100644 --- a/src/logic/Utils.ts +++ b/src/logic/Utils.ts @@ -1,9 +1,9 @@ import Chroma from "chroma-js"; import { t } from "i18next"; -import { CardProps } from "../components/Card/Card"; -import { Author, CardItem, ColourScheme, SchemeIni, Snippet, SortBoxOption, ResetCategory } from "../types/marketplace-types"; +import type { CardProps } from "../components/Card/Card"; import { LOCALSTORAGE_KEYS } from "../constants"; +import type { Author, CardItem, ColourScheme, ResetCategory, SchemeIni, Snippet, SortBoxOption } from "../types/marketplace-types"; /** * Get localStorage data (or fallback value), given a key @@ -31,12 +31,20 @@ export const getLocalStorageDataFromKey = (key: string, fallback?: unknown) => { * @param hex 3 or 6 character hex string * @returns Array of RGB values */ -const hexToRGB = (hex: string) => { - if (hex.length === 3) { - hex = hex.split("").map((char) => char + char).join(""); - } else if (hex.length != 6) { +const hexToRGB = (inputHex: string) => { + const hex = + inputHex.length === 3 + ? inputHex + .split("") + .map((char) => char + char) + .join("") + : inputHex; + + if (hex.length !== 6) { throw "Only 3- or 6-digit hex colours are allowed."; - } else if (hex.match(/[^0-9a-f]/i)) { + } + + if (hex.match(/[^0-9a-f]/i)) { throw "Only hex colours are allowed."; } @@ -45,38 +53,35 @@ const hexToRGB = (hex: string) => { throw "Could not parse hex colour."; } - const aRgb = [ - parseInt(aRgbHex[0], 16), - parseInt(aRgbHex[1], 16), - parseInt(aRgbHex[2], 16), - ]; + const aRgb = [Number.parseInt(aRgbHex[0], 16), Number.parseInt(aRgbHex[1], 16), Number.parseInt(aRgbHex[2], 16)]; return aRgb; }; /** -* Parse INI file into a colour scheme object -* @param data The INI file string data -* @returns Object containing the parsed colour schemes -*/ -export const parseIni = (data: string) => { + * Parse INI file into a colour scheme object + * @param data The INI file string data + * @returns Object containing the parsed colour schemes + */ +export const parseIni = (data: string): SchemeIni => { const regex = { section: /^\s*\[\s*([^\]]*)\s*\]\s*$/, param: /^\s*([^=]+?)\s*=\s*(.*?)\s*$/, - comment: /^\s*;.*$/, + comment: /^\s*;.*$/ }; const value = {}; const lines = data.split(/[\r\n]+/); let section: string | null = null; - lines.forEach(function(line) { + for (const line of lines) { if (regex.comment.test(line)) { - return; - } else if (regex.param.test(line)) { + return {}; + } + if (regex.param.test(line)) { // Discard color scheme if it contains xrdb if (line.includes("xrdb")) { delete value[section ?? ""]; section = null; - return; + return {}; } const match: string[] | null = line.match(regex.param); @@ -95,10 +100,10 @@ export const parseIni = (data: string) => { value[match[1]] = {}; section = match[1]; } - } else if (line.length == 0 && section) { + } else if (line.length === 0 && section) { section = null; } - }); + } return value; }; @@ -123,9 +128,9 @@ export const unparseIni = (data: SchemeIni) => { }; /** -* Loop through the snippets and add the contents of the code as a style tag in the DOM -* @param snippets The snippets to initialize -*/ + * Loop through the snippets and add the contents of the code as a style tag in the DOM + * @param snippets The snippets to initialize + */ // TODO: keep this in sync with the extension.js file export const initializeSnippets = (snippets: Snippet[]) => { // Remove any existing marketplace snippets @@ -134,9 +139,7 @@ export const initializeSnippets = (snippets: Snippet[]) => { const style = document.createElement("style"); const styleContent = snippets.reduce((accum, snippet) => { - accum += `/* ${snippet.title} - ${snippet.description} */\n`; - accum += `${snippet.code}\n`; - return accum; + return `${accum}/* ${snippet.title} - ${snippet.description} */\n${snippet.code}\n`; }, ""); style.innerHTML = styleContent; @@ -169,12 +172,12 @@ export const processAuthors = (authors: Author[], user: string) => { if (authors && authors.length > 0) { parsedAuthors = authors.map((author) => ({ name: author.name, - url: sanitizeUrl(author.url), + url: sanitizeUrl(author.url) })); } else { parsedAuthors.push({ name: user, - url: "https://github.com/" + user, + url: `https://github.com/${user}` }); } @@ -182,16 +185,14 @@ export const processAuthors = (authors: Author[], user: string) => { }; /** -* Generate a list of options for the schemes dropdown. -* @param schemes The schemes object from the theme. -* @returns Array of options for the schemes dropdown. -*/ + * Generate a list of options for the schemes dropdown. + * @param schemes The schemes object from the theme. + * @returns Array of options for the schemes dropdown. + */ export const generateSchemesOptions = (schemes: SchemeIni) => { // e.g. [ { key: "red", value: "Red" }, { key: "dark", value: "Dark" } ] if (!schemes) return []; - return Object.keys(schemes).map(schemeName => ( - { key: schemeName, value: schemeName } as SortBoxOption - )); + return Object.keys(schemes).map((schemeName) => ({ key: schemeName, value: schemeName }) as SortBoxOption); }; /** @@ -213,7 +214,7 @@ export const generateSortOptions = (t: (key: string) => string) => { { key: "lastUpdated", value: t("grid.sort.lastUpdated") }, { key: "mostStale", value: t("grid.sort.mostStale") }, { key: "a-z", value: t("grid.sort.aToZ") }, - { key: "z-a", value: t("grid.sort.zToA") }, + { key: "z-a", value: t("grid.sort.zToA") } ]; }; @@ -230,44 +231,44 @@ export const resetMarketplace = (...categories: ResetCategory[]) => { if (categories.length === 0) { // Loop through all marketplace keys. // This includes extensions, themes, and snippets, as well as the Marketplace settings. - Object.keys(localStorage).forEach((key) => { + for (const key in localStorage) { if (key.startsWith("marketplace:")) { keysToRemove.push(key); } - }); + } } // If have categories, reset only those - categories.forEach((category) => { + for (const category of categories) { switch (category) { - case "extensions": - // Remove the extensions themselves - keysToRemove.push(...getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedExtensions, [])); - // Remove the list of extension keys - keysToRemove.push(LOCALSTORAGE_KEYS.installedExtensions); - break; - - case "snippets": - keysToRemove.push(...getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedSnippets, [])); - keysToRemove.push(LOCALSTORAGE_KEYS.installedSnippets); - break; - - case "theme": - keysToRemove.push(...getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedThemes, [])); - keysToRemove.push(LOCALSTORAGE_KEYS.installedThemes); - keysToRemove.push(LOCALSTORAGE_KEYS.themeInstalled); - break; - - default: - console.error(`Unknown category: ${category}`); - break; + case "extensions": + // Remove the extensions themselves + keysToRemove.push(...getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedExtensions, [])); + // Remove the list of extension keys + keysToRemove.push(LOCALSTORAGE_KEYS.installedExtensions); + break; + + case "snippets": + keysToRemove.push(...getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedSnippets, [])); + keysToRemove.push(LOCALSTORAGE_KEYS.installedSnippets); + break; + + case "theme": + keysToRemove.push(...getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.installedThemes, [])); + keysToRemove.push(LOCALSTORAGE_KEYS.installedThemes); + keysToRemove.push(LOCALSTORAGE_KEYS.themeInstalled); + break; + + default: + console.error(`Unknown category: ${category}`); + break; } - }); + } - keysToRemove.forEach((key) => { + for (const key of keysToRemove) { localStorage.removeItem(key); console.debug(`Removed ${key}`); - }); + } console.debug("Marketplace has been reset"); location.reload(); @@ -277,24 +278,24 @@ export const exportMarketplace = () => { // TODO: Export settings, extensions, snippets, themes, colour scheme const data = {}; - Object.keys(localStorage).forEach((key) => { + for (const key in localStorage) { // console.log(`${key}: ${localStorage.getItem(key)}`); if (key.startsWith("marketplace:")) { data[key] = localStorage.getItem(key); } - }); + } return data as JSON; }; -export const importMarketplace = (data : JSON) => { +export const importMarketplace = (data: JSON) => { console.debug("Importing Marketplace"); // First reset the marketplace resetMarketplace(); // Then import the data - Object.keys(data).forEach((key) => { + for (const key in data) { localStorage.setItem(key, data[key]); console.debug(`Imported ${key}`); - }); + } }; // NOTE: Keep in sync with extension.js @@ -311,10 +312,10 @@ export const injectColourScheme = (scheme: ColourScheme | null) => { let injectStr = ":root {"; const themeIniKeys = Object.keys(scheme); - themeIniKeys.forEach((key) => { + for (const key of themeIniKeys) { injectStr += `--spice-${key}: #${scheme[key]};`; injectStr += `--spice-rgb-${key}: ${hexToRGB(scheme[key])};`; - }); + } injectStr += "}"; schemeTag.innerHTML = injectStr; document.body.appendChild(schemeTag); @@ -373,7 +374,7 @@ export const getColorFromImage = async (image: string) => { let vibrancy = getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.albumArtBasedColorVibrancy); // Add a underscore before any uppercase characters, then make the whole string uppercase vibrancy = vibrancy.replace(/([A-Z])/g, "_$1").toUpperCase(); - const colorOptions = (await Spicetify.colorExtractor(image)); + const colorOptions = await Spicetify.colorExtractor(image); const color = colorOptions[vibrancy]; return color.substring(1); }; @@ -383,8 +384,9 @@ export const generateColorPalette = async (mainColor: string, numColors: number) // Add a hyphen before any uppercase characters const modeStr = mode.replace(/([A-Z])/g, "-$1").toLowerCase(); //fetch `https://www.thecolorapi.com/scheme?hex=${mainColor}&mode=${modeStr}&count=${numColors}` - const palette = await fetch(`https://www.thecolorapi.com/scheme?hex=${mainColor}&mode=${modeStr}&count=${numColors}`) - .then((response) => response.json()); + const palette = await fetch(`https://www.thecolorapi.com/scheme?hex=${mainColor}&mode=${modeStr}&count=${numColors}`).then((response) => + response.json() + ); // create an array of the hex values for the colors while also removing the # const colorArray = palette.colors.map((color) => color.hex.value.substring(1)); return colorArray; @@ -407,7 +409,7 @@ export const initAlbumArtBasedColor = (scheme: ColourScheme) => { // and update the color scheme accordingly Spicetify.Player.addEventListener("songchange", async () => { await sleep(1000); - let albumArtSrc = Spicetify.Player.data?.item?.metadata?.image_xlarge_url; + let albumArtSrc: string | undefined = Spicetify.Player.data?.item?.metadata?.image_xlarge_url; // If it doesn't exist, wait for it to load if (albumArtSrc == null) { @@ -432,11 +434,13 @@ export const initAlbumArtBasedColor = (scheme: ColourScheme) => { } } // Order the color map by how similar the colors are to eachother - const orderedColorMap = new Map([...colorMap.entries()].sort((a, b) => { - const aColor = Chroma(a[0]); - const bColor = Chroma(b[0]); - return aColor.get("lab.l") - bColor.get("lab.l"); - })); + const orderedColorMap = new Map( + [...colorMap.entries()].sort((a, b) => { + const aColor = Chroma(a[0]); + const bColor = Chroma(b[0]); + return aColor.get("lab.l") - bColor.get("lab.l"); + }) + ); colorMap = orderedColorMap; // replace the keys in the color map with the new colors const newScheme = {}; @@ -453,20 +457,20 @@ export const initAlbumArtBasedColor = (scheme: ColourScheme) => { }); }; -export const parseCSS = async (themeData: CardItem, tld?: string) => { +export const parseCSS = async (themeData: CardItem, defaultTld?: string) => { if (!themeData.cssURL) throw new Error("No CSS URL provided"); - tld ||= await getAvailableTLD(); + const tld = defaultTld || (await getAvailableTLD()); const userCssUrl = isGithubRawUrl(themeData.cssURL) - // TODO: this should probably be the URL stored in localstorage actually (i.e. put this url in localstorage) - ? `https://cdn.jsdelivr.${tld}/gh/${themeData.user}/${themeData.repo}@${themeData.branch}/${themeData.manifest.usercss}` + ? // TODO: this should probably be the URL stored in localstorage actually (i.e. put this url in localstorage) + `https://cdn.jsdelivr.${tld}/gh/${themeData.user}/${themeData.repo}@${themeData.branch}/${themeData.manifest.usercss}` : themeData.cssURL; // TODO: Make this more versatile const assetsUrl = userCssUrl.replace("/user.css", "/assets/"); console.debug("Parsing CSS: ", userCssUrl); - let css = await fetch(`${userCssUrl}?time=${Date.now()}`).then(res => res.text()); + let css = await fetch(`${userCssUrl}?time=${Date.now()}`).then((res) => res.text()); // console.log("Parsed CSS: ", css); const urls = css.matchAll(/url\(['|"](?.+?)['|"]\)/gm) || []; @@ -492,7 +496,7 @@ export const isGithubRawUrl = (url: string) => { const parsedUrl = new URL(url); parsedUrl.host; - return (parsedUrl.host === "raw.githubusercontent.com"); + return parsedUrl.host === "raw.githubusercontent.com"; }; /** @@ -508,7 +512,7 @@ export const getParamsFromGithubRaw = (url: string) => { user: regex_result ? regex_result.groups?.user : null, repo: regex_result ? regex_result.groups?.repo : null, branch: regex_result ? regex_result.groups?.branch : null, - filePath: regex_result ? regex_result.groups?.filePath : null, + filePath: regex_result ? regex_result.groups?.filePath : null }; return obj; @@ -516,7 +520,7 @@ export const getParamsFromGithubRaw = (url: string) => { export function addToSessionStorage(items, key?) { if (!items) return; - items.forEach((item) => { + for (const item of items) { const itemKey = key || `${item.user}-${item.repo}`; // If the key already exists, it will append to it instead of overwriting it @@ -524,12 +528,12 @@ export function addToSessionStorage(items, key?) { const parsed = existing ? JSON.parse(existing) : []; parsed.push(item); window.sessionStorage.setItem(itemKey, JSON.stringify(parsed)); - }); + } } export function getInvalidCSS(): string[] { const unparsedCSS = document.querySelector("body > style.marketplaceCSS.marketplaceUserCSS"); const classNameList = unparsedCSS?.innerHTML; - const regex = new RegExp (`.-?[_a-zA-Z]+[_a-zA-Z0-9-]*\\s*{`, "g"); + const regex = /.-?[_a-zA-Z]+[_a-zA-Z0-9-]*\s*{/g; if (!classNameList) return ["Error: Class name list not found; please create an issue"]; const matches = classNameList.matchAll(regex); const invalidCssClassName: string[] = []; @@ -537,12 +541,11 @@ export function getInvalidCSS(): string[] { // Check if match is the same class name as an html element const className = match[0].replace(/{/g, "").trim(); const classesArr = className.split(" "); - let element; + let element: HTMLCollectionOf | Element | null; for (let i = 0; i < classesArr.length; i++) { try { element = document.querySelector(`${classesArr[i]}`); - } - catch (e) { + } catch (e) { element = document.getElementsByClassName(`${className}`); } if (!element) { @@ -558,17 +561,14 @@ export async function getMarkdownHTML(markdown: string, user: string, repo: stri const postBody = { text: markdown, context: `${user}/${repo}`, - mode: "gfm", + mode: "gfm" }; const response = await fetch("https://api.github.com/markdown", { method: "POST", - body: JSON.stringify(postBody), + body: JSON.stringify(postBody) }); - if (!response.ok) throw Spicetify.showNotification( - t("notifications.markdownParsingError", { status: response.status }), - true, - ); + if (!response.ok) throw Spicetify.showNotification(t("notifications.markdownParsingError", { status: response.status }), true); const html = await response.text(); @@ -580,7 +580,7 @@ export async function getMarkdownHTML(markdown: string, user: string, repo: stri // This function is used to sleep for a certain amount of time export function sleep(ms: number | undefined) { - return new Promise(resolve => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } export function generateKey(props: CardProps) { @@ -588,18 +588,18 @@ export function generateKey(props: CardProps) { let cardId: string; switch (props.type) { - case "snippet": - cardId = props.item.title.replaceAll(" ", "-"); - break; - case "theme": - cardId = props.item.manifest?.usercss || ""; - break; - case "extension": - cardId = props.item.manifest?.main || ""; - break; - case "app": - cardId = props.item.manifest?.name?.replaceAll(" ", "-") || ""; - break; + case "snippet": + cardId = props.item.title.replaceAll(" ", "-"); + break; + case "theme": + cardId = props.item.manifest?.usercss || ""; + break; + case "extension": + cardId = props.item.manifest?.main || ""; + break; + case "app": + cardId = props.item.manifest?.name?.replaceAll(" ", "-") || ""; + break; } return `marketplace:installed:${prefix}${cardId}`; @@ -607,8 +607,7 @@ export function generateKey(props: CardProps) { export const sanitizeUrl = (url: string) => { const u = decodeURI(url).trim().toLowerCase(); - if (u.startsWith("javascript:") || u.startsWith("data:") || u.startsWith("vbscript:")) - return "about:blank"; + if (u.startsWith("javascript:") || u.startsWith("data:") || u.startsWith("vbscript:")) return "about:blank"; return url; }; @@ -660,28 +659,27 @@ const compareUpdated = (a: CardItem | Snippet, b: CardItem | Snippet) => { export const sortCardItems = (cardItems: CardItem[] | Snippet[], sortMode: string) => { switch (sortMode) { - case "a-z": - cardItems.sort((a, b) => compareNames(a, b)); - break; - case "z-a": - cardItems.sort((a, b) => compareNames(b, a)); - break; - case "newest": - cardItems.sort((a, b) => compareCreated(a, b)); - break; - case "oldest": - cardItems.sort((a, b) => compareCreated(b, a)); - break; - case "lastUpdated": - cardItems.sort((a, b) => compareUpdated(a, b)); - break; - case "mostStale": - cardItems.sort((a, b) => compareUpdated(b, a)); - break; - case "stars": - default: - cardItems.sort((a, b) => b.stars - a.stars); - break; + case "a-z": + cardItems.sort((a, b) => compareNames(a, b)); + break; + case "z-a": + cardItems.sort((a, b) => compareNames(b, a)); + break; + case "newest": + cardItems.sort((a, b) => compareCreated(a, b)); + break; + case "oldest": + cardItems.sort((a, b) => compareCreated(b, a)); + break; + case "lastUpdated": + cardItems.sort((a, b) => compareUpdated(a, b)); + break; + case "mostStale": + cardItems.sort((a, b) => compareUpdated(b, a)); + break; + default: + cardItems.sort((a, b) => b.stars - a.stars); + break; } }; @@ -695,7 +693,6 @@ export async function getAvailableTLD() { if (response.type === "opaqueredirect") return tld; } catch (err) { console.error(err); - continue; } } } diff --git a/src/resources/locales/index.ts b/src/resources/locales/index.ts index 721e178c..553487b4 100644 --- a/src/resources/locales/index.ts +++ b/src/resources/locales/index.ts @@ -1,16 +1,16 @@ import ca from "./ca.json"; -import en from "./en.json"; import enUS from "./en-US.json"; +import en from "./en.json"; import es from "./es.json"; -import fr from "./fr.json"; -import zhTW from "./zh-TW.json"; -import zhCN from "./zh-CN.json"; -import ru from "./ru.json"; import et from "./et.json"; -import pl from "./pl.json"; +import fr from "./fr.json"; import it from "./it.json"; -import uk from "./uk.json"; import ja from "./ja.json"; +import pl from "./pl.json"; +import ru from "./ru.json"; +import uk from "./uk.json"; +import zhCN from "./zh-CN.json"; +import zhTW from "./zh-TW.json"; export default { ca, @@ -25,5 +25,5 @@ export default { pl, it, uk, - ja, + ja }; diff --git a/src/resources/locales/uk.json b/src/resources/locales/uk.json index a0a6f9c8..e42f7f42 100644 --- a/src/resources/locales/uk.json +++ b/src/resources/locales/uk.json @@ -76,7 +76,7 @@ }, "updateModal": { "title": "Оновити Маркетплейс", - "description": "Оновіть Spicetify Marketplace щоб отримувати нові функції і багфікси.", + "description": "Оновіть Spicetify Marketplace щоб отримувати нові функції і багфікси.", "currentVersion": "Поточна версія: {{version}}", "latestVersion": "Остання версія: {{version}}", "whatsChanged": "Що змінилося", @@ -119,9 +119,9 @@ "colour_other": "кольори", "favourite": "улюблене", "notifications": { - "wrongLocalTheme": "Будь ласка, поставте 'marketplace' у змінну current_theme у файлі config-xpui.ini щоб встановлювати теми за допомогою Маркетплейсу", + "wrongLocalTheme": "Будь ласка, поставте 'marketplace' у змінну current_theme у файлі config-xpui.ini щоб встановлювати теми за допомогою Маркетплейсу", "tooManyRequests": "Забагато запитів, зачекайте", - "noCdnConnection": "Маркетплейс не може зв'язатися з CDN. Будь ласка, перевірте вашу конфігурацію Інтернету", + "noCdnConnection": "Маркетплейс не може зв'язатися з CDN. Будь ласка, перевірте вашу конфігурацію Інтернету", "markdownParsingError": "Помилка розбору markdown (HTTP {{status}})", "noReadmeFile": "README не знайдено", "themeInstallationError": "Сталася помилка при встановленні теми", diff --git a/src/types/marketplace-types.d.ts b/src/types/marketplace-types.d.ts index e4dc7d5f..20d79cb6 100644 --- a/src/types/marketplace-types.d.ts +++ b/src/types/marketplace-types.d.ts @@ -15,17 +15,9 @@ export type SortBoxOption = { value: string; }; -export type RepoTopic = - | "spicetify-extensions" - | "spicetify-themes" - | "spicetify-apps"; - -export type TabType = - | "Extensions" - | "Themes" - | "Snippets" - | "Apps" - | "Installed"; +export type RepoTopic = "spicetify-extensions" | "spicetify-themes" | "spicetify-apps"; + +export type TabType = "Extensions" | "Themes" | "Snippets" | "Apps" | "Installed"; export type ResetCategory = "extensions" | "snippets" | "theme"; @@ -133,18 +125,8 @@ export type VisualConfig = { colorShift: boolean; themeDevTools: boolean; albumArtBasedColors: boolean; - albumArtBasedColorsMode: - | "monochromeLight" - | "monochromeDark" - | "quad" - | "triad" - | "analogic" - | "analogicComplement"; - albumArtBasedColorsVibrancy: - | "DESATURATED" - | "LIGHT_VIBRANT" - | "PROMINENT" - | "VIBRANT"; + albumArtBasedColorsMode: "monochromeLight" | "monochromeDark" | "quad" | "triad" | "analogic" | "analogicComplement"; + albumArtBasedColorsVibrancy: "DESATURATED" | "LIGHT_VIBRANT" | "PROMINENT" | "VIBRANT"; // Legacy from reddit app type: boolean; // I was considering adding watchers as "followers" but it looks like the value is a duplicate @@ -176,14 +158,7 @@ export type SchemeIni = { [key: string]: ColourScheme; }; -export type SortMode = - | "a-z" - | "z-a" - | "newest" - | "oldest" - | "stars" - | "lastUpdated" - | "mostStale"; +export type SortMode = "a-z" | "z-a" | "newest" | "oldest" | "stars" | "lastUpdated" | "mostStale"; export type Config = { // Fetch the settings and set defaults. Used in Settings.js diff --git a/src/types/spicetify.d.ts b/src/types/spicetify.d.ts index 56f76667..2a9d3fe8 100644 --- a/src/types/spicetify.d.ts +++ b/src/types/spicetify.d.ts @@ -1,2370 +1,2370 @@ declare namespace Spicetify { - type Icon = - | "album" - | "artist" - | "block" - | "brightness" - | "car" - | "chart-down" - | "chart-up" - | "check" - | "check-alt-fill" - | "chevron-left" - | "chevron-right" - | "chromecast-disconnected" - | "clock" - | "collaborative" - | "computer" - | "copy" - | "download" - | "downloaded" - | "edit" - | "enhance" - | "exclamation-circle" - | "external-link" - | "facebook" - | "follow" - | "fullscreen" - | "gamepad" - | "grid-view" - | "heart" - | "heart-active" - | "instagram" - | "laptop" - | "library" - | "list-view" - | "location" - | "locked" - | "locked-active" - | "lyrics" - | "menu" - | "minimize" - | "minus" - | "more" - | "new-spotify-connect" - | "offline" - | "pause" - | "phone" - | "play" - | "playlist" - | "playlist-folder" - | "plus-alt" - | "plus2px" - | "podcasts" - | "projector" - | "queue" - | "repeat" - | "repeat-once" - | "search" - | "search-active" - | "shuffle" - | "skip-back" - | "skip-back15" - | "skip-forward" - | "skip-forward15" - | "soundbetter" - | "speaker" - | "spotify" - | "subtitles" - | "tablet" - | "ticket" - | "twitter" - | "visualizer" - | "voice" - | "volume" - | "volume-off" - | "volume-one-wave" - | "volume-two-wave" - | "watch" - | "x"; - type Variant = - | "bass" - | "forte" - | "brio" - | "altoBrio" - | "alto" - | "canon" - | "celloCanon" - | "cello" - | "ballad" - | "balladBold" - | "viola" - | "violaBold" - | "mesto" - | "mestoBold" - | "metronome" - | "finale" - | "finaleBold" - | "minuet" - | "minuetBold"; - type SemanticColor = - | "textBase" - | "textSubdued" - | "textBrightAccent" - | "textNegative" - | "textWarning" - | "textPositive" - | "textAnnouncement" - | "essentialBase" - | "essentialSubdued" - | "essentialBrightAccent" - | "essentialNegative" - | "essentialWarning" - | "essentialPositive" - | "essentialAnnouncement" - | "decorativeBase" - | "decorativeSubdued" - | "backgroundBase" - | "backgroundHighlight" - | "backgroundPress" - | "backgroundElevatedBase" - | "backgroundElevatedHighlight" - | "backgroundElevatedPress" - | "backgroundTintedBase" - | "backgroundTintedHighlight" - | "backgroundTintedPress" - | "backgroundUnsafeForSmallTextBase" - | "backgroundUnsafeForSmallTextHighlight" - | "backgroundUnsafeForSmallTextPress"; - type ColorSet = - | "base" - | "brightAccent" - | "negative" - | "warning" - | "positive" - | "announcement" - | "invertedDark" - | "invertedLight" - | "mutedAccent" - | "overMedia"; - type ColorSetBackgroundColors = { - base: string; - highlight: string; - press: string; - }; - type ColorSetNamespaceColors = { - announcement: string; - base: string; - brightAccent: string; - negative: string; - positive: string; - subdued: string; - warning: string; - }; - type ColorSetBody = { - background: ColorSetBackgroundColors & { - elevated: ColorSetBackgroundColors; - tinted: ColorSetBackgroundColors; - unsafeForSmallText: ColorSetBackgroundColors; - }; - decorative: { - base: string; - subdued: string; - }; - essential: ColorSetNamespaceColors; - text: ColorSetNamespaceColors; - }; - type Metadata = Partial>; - type ContextTrack = { - uri: string; - uid?: string; - metadata?: Metadata; - }; - type PlayerState = { - timestamp: number; - context: PlayerContext; - index: PlayerIndex; - item: PlayerTrack; - shuffle: boolean; - repeat: number; - speed: number; - positionAsOfTimestamp: number; - duration: number; - hasContext: boolean; - isPaused: boolean; - isBuffering: boolean; - restrictions: Restrictions; - previousItems?: PlayerTrack[]; - nextItems?: PlayerTrack[]; - playbackQuality: PlaybackQuality; - playbackId: string; - sessionId: string; - signals?: any[]; - }; - type PlayerContext = { - uri: string; - url: string; - metadata: { - "player.arch": string; - }; - }; - type PlayerIndex = { - pageURI?: string | null; - pageIndex: number; - itemIndex: number; - }; - type PlayerTrack = { - type: string; - uri: string; - uid: string; - name: string; - mediaType: string; - duration: { - milliseconds: number; - }; - album: Album; - artists?: ArtistsEntity[]; - isLocal: boolean; - isExplicit: boolean; - is19PlusOnly: boolean; - provider: string; - metadata: TrackMetadata; - images?: ImagesEntity[]; - }; - type TrackMetadata = { - artist_uri: string; - entity_uri: string; - iteration: string; - title: string; - "collection.is_banned": string; - "artist_uri:1": string; - "collection.in_collection": string; - image_small_url: string; - "collection.can_ban": string; - is_explicit: string; - album_disc_number: string; - album_disc_count: string; - track_player: string; - album_title: string; - "collection.can_add": string; - image_large_url: string; - "actions.skipping_prev_past_track": string; - page_instance_id: string; - image_xlarge_url: string; - marked_for_download: string; - "actions.skipping_next_past_track": string; - context_uri: string; - "artist_name:1": string; - has_lyrics: string; - interaction_id: string; - image_url: string; - album_uri: string; - album_artist_name: string; - album_track_number: string; - artist_name: string; - duration: string; - album_track_count: string; - popularity: string; - }; - type Album = { - type: string; - uri: string; - name: string; - images?: ImagesEntity[]; - }; - type ImagesEntity = { - url: string; - label: string; - }; - type ArtistsEntity = { - type: string; - uri: string; - name: string; - }; - type Restrictions = { - canPause: boolean; - canResume: boolean; - canSeek: boolean; - canSkipPrevious: boolean; - canSkipNext: boolean; - canToggleRepeatContext: boolean; - canToggleRepeatTrack: boolean; - canToggleShuffle: boolean; - disallowPausingReasons?: string[]; - disallowResumingReasons?: string[]; - disallowSeekingReasons?: string[]; - disallowSkippingPreviousReasons?: string[]; - disallowSkippingNextReasons?: string[]; - disallowTogglingRepeatContextReasons?: string[]; - disallowTogglingRepeatTrackReasons?: string[]; - disallowTogglingShuffleReasons?: string[]; - disallowTransferringPlaybackReasons?: string[]; - }; - type PlaybackQuality = { - bitrateLevel: number; - strategy: number; - targetBitrateLevel: number; - targetBitrateAvailable: boolean; - hifiStatus: number; - }; - namespace Player { - /** - * Register a listener `type` on Spicetify.Player. - * - * On default, `Spicetify.Player` always dispatch: - * - `songchange` type when player changes track. - * - `onplaypause` type when player plays or pauses. - * - `onprogress` type when track progress changes. - * - `appchange` type when user changes page. - */ - function addEventListener(type: string, callback: (event?: Event) => void): void; - function addEventListener(type: "songchange", callback: (event?: Event & { data: PlayerState }) => void): void; - function addEventListener(type: "onplaypause", callback: (event?: Event & { data: PlayerState }) => void): void; - function addEventListener(type: "onprogress", callback: (event?: Event & { data: number }) => void): void; - function addEventListener( - type: "appchange", - callback: ( - event?: Event & { - data: { - /** - * App href path - */ - path: string; - /** - * App container - */ - container: HTMLElement; - }; - } - ) => void - ): void; - /** - * Skip to previous track. - */ - function back(): void; - /** - * An object contains all information about current track and player. - */ - const data: PlayerState; - /** - * Decrease a small amount of volume. - */ - function decreaseVolume(): void; - /** - * Dispatches an event at `Spicetify.Player`. - * - * On default, `Spicetify.Player` always dispatch - * - `songchange` type when player changes track. - * - `onplaypause` type when player plays or pauses. - * - `onprogress` type when track progress changes. - * - `appchange` type when user changes page. - */ - function dispatchEvent(event: Event): void; - const eventListeners: { - [key: string]: Array<(event?: Event) => void>; - }; - /** - * Convert milisecond to `mm:ss` format - * @param milisecond - */ - function formatTime(milisecond: number): string; - /** - * Return song total duration in milisecond. - */ - function getDuration(): number; - /** - * Return mute state - */ - function getMute(): boolean; - /** - * Return elapsed duration in milisecond. - */ - function getProgress(): number; - /** - * Return elapsed duration in percentage (0 to 1). - */ - function getProgressPercent(): number; - /** - * Return current Repeat state (No repeat = 0/Repeat all = 1/Repeat one = 2). - */ - function getRepeat(): number; - /** - * Return current shuffle state. - */ - function getShuffle(): boolean; - /** - * Return track heart state. - */ - function getHeart(): boolean; - /** - * Return current volume level (0 to 1). - */ - function getVolume(): number; - /** - * Increase a small amount of volume. - */ - function increaseVolume(): void; - /** - * Return a boolean whether player is playing. - */ - function isPlaying(): boolean; - /** - * Skip to next track. - */ - function next(): void; - /** - * Pause track. - */ - function pause(): void; - /** - * Resume track. - */ - function play(): void; - /** - * Play a track, playlist, album, etc. immediately - * @param uri Spotify URI - * @param context - * @param options - */ - function playUri(uri: string, context?: any, options?: any): Promise; - /** - * Unregister added event listener `type`. - * @param type - * @param callback - */ - function removeEventListener(type: string, callback: (event?: Event) => void): void; - /** - * Seek track to position. - * @param position can be in percentage (0 to 1) or in milisecond. - */ - function seek(position: number): void; - /** - * Turn mute on/off - * @param state - */ - function setMute(state: boolean): void; - /** - * Change Repeat mode - * @param mode `0` No repeat. `1` Repeat all. `2` Repeat one track. - */ - function setRepeat(mode: number): void; - /** - * Turn shuffle on/off. - * @param state - */ - function setShuffle(state: boolean): void; - /** - * Set volume level - * @param level 0 to 1 - */ - function setVolume(level: number): void; - /** - * Seek to previous `amount` of milisecond - * @param amount in milisecond. Default: 15000. - */ - function skipBack(amount?: number): void; - /** - * Seek to next `amount` of milisecond - * @param amount in milisecond. Default: 15000. - */ - function skipForward(amount?: number): void; - /** - * Toggle Heart (Favourite) track state. - */ - function toggleHeart(): void; - /** - * Toggle Mute/No mute. - */ - function toggleMute(): void; - /** - * Toggle Play/Pause. - */ - function togglePlay(): void; - /** - * Toggle No repeat/Repeat all/Repeat one. - */ - function toggleRepeat(): void; - /** - * Toggle Shuffle/No shuffle. - */ - function toggleShuffle(): void; - } - /** - * Adds a track or array of tracks to prioritized queue. - */ - function addToQueue(uri: ContextTrack[]): Promise; - /** - * @deprecated - */ - const BridgeAPI: any; - /** - * @deprecated - */ - const CosmosAPI: any; - /** - * Async wrappers of CosmosAPI - */ - namespace CosmosAsync { - type Method = "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | "SUB"; - interface Error { - code: number; - error: string; - message: string; - stack?: string; - } - - type Headers = Record; - type Body = Record; - - interface Response { - body: any; - headers: Headers; - status: number; - uri?: string; - } - - function head(url: string, headers?: Headers): Promise; - function get(url: string, body?: Body, headers?: Headers): Promise; - function post(url: string, body?: Body, headers?: Headers): Promise; - function put(url: string, body?: Body, headers?: Headers): Promise; - function del(url: string, body?: Body, headers?: Headers): Promise; - function patch(url: string, body?: Body, headers?: Headers): Promise; - function sub( - url: string, - callback: (b: Response["body"]) => void, - onError?: (e: Error) => void, - body?: Body, - headers?: Headers - ): Promise; - function postSub( - url: string, - body: Body | null, - callback: (b: Response["body"]) => void, - onError?: (e: Error) => void - ): Promise; - function request(method: Method, url: string, body?: Body, headers?: Headers): Promise; - function resolve(method: Method, url: string, body?: Body, headers?: Headers): Promise; - } - /** - * Fetch interesting colors from URI. - * @param uri Any type of URI that has artwork (playlist, track, album, artist, show, ...) - */ - function colorExtractor(uri: string): Promise<{ - DESATURATED: string; - LIGHT_VIBRANT: string; - PROMINENT: string; - VIBRANT: string; - VIBRANT_NON_ALARMING: string; - }>; - /** - * @deprecated - */ - function getAblumArtColors(): any; - /** - * Fetch track analyzed audio data. - * Beware, not all tracks have audio data. - * @param uri is optional. Leave it blank to get current track - * or specify another track uri. - */ - function getAudioData(uri?: string): Promise; - /** - * Set of APIs method to register, deregister hotkeys/shortcuts - */ - namespace Keyboard { - type ValidKey = - | "BACKSPACE" - | "TAB" - | "ENTER" - | "SHIFT" - | "CTRL" - | "ALT" - | "CAPS" - | "ESCAPE" - | "SPACE" - | "PAGE_UP" - | "PAGE_DOWN" - | "END" - | "HOME" - | "ARROW_LEFT" - | "ARROW_UP" - | "ARROW_RIGHT" - | "ARROW_DOWN" - | "INSERT" - | "DELETE" - | "A" - | "B" - | "C" - | "D" - | "E" - | "F" - | "G" - | "H" - | "I" - | "J" - | "K" - | "L" - | "M" - | "N" - | "O" - | "P" - | "Q" - | "R" - | "S" - | "T" - | "U" - | "V" - | "W" - | "X" - | "Y" - | "Z" - | "WINDOW_LEFT" - | "WINDOW_RIGHT" - | "SELECT" - | "NUMPAD_0" - | "NUMPAD_1" - | "NUMPAD_2" - | "NUMPAD_3" - | "NUMPAD_4" - | "NUMPAD_5" - | "NUMPAD_6" - | "NUMPAD_7" - | "NUMPAD_8" - | "NUMPAD_9" - | "MULTIPLY" - | "ADD" - | "SUBTRACT" - | "DECIMAL_POINT" - | "DIVIDE" - | "F1" - | "F2" - | "F3" - | "F4" - | "F5" - | "F6" - | "F7" - | "F8" - | "F9" - | "F10" - | "F11" - | "F12" - | ";" - | "=" - | " | " - | "-" - | "." - | "/" - | "`" - | "[" - | "\\" - | "]" - | '"' - | "~" - | "!" - | "@" - | "#" - | "$" - | "%" - | "^" - | "&" - | "*" - | "(" - | ")" - | "_" - | "+" - | ":" - | "<" - | ">" - | "?" - | "|"; - type KeysDefine = - | string - | { - key: string; - ctrl?: boolean; - shift?: boolean; - alt?: boolean; - meta?: boolean; - }; - const KEYS: Record; - function registerShortcut(keys: KeysDefine, callback: (event: KeyboardEvent) => void): void; - function registerIsolatedShortcut(keys: KeysDefine, callback: (event: KeyboardEvent) => void): void; - function registerImportantShortcut(keys: KeysDefine, callback: (event: KeyboardEvent) => void): void; - function _deregisterShortcut(keys: KeysDefine): void; - function deregisterImportantShortcut(keys: KeysDefine): void; - function changeShortcut(keys: KeysDefine, newKeys: KeysDefine): void; - } - - /** - * @deprecated - */ - const LiveAPI: any; - - namespace LocalStorage { - /** - * Empties the list associated with the object of all key/value pairs, if there are any. - */ - function clear(): void; - /** - * Get key value - */ - function get(key: string): string | null; - /** - * Delete key - */ - function remove(key: string): void; - /** - * Set new value for key - */ - function set(key: string, value: string): void; - } - /** - * To create and prepend custom menu item in profile menu. - */ - namespace Menu { - /** - * Create a single toggle. - */ - class Item { - constructor(name: string, isEnabled: boolean, onClick: (self: Item) => void, icon?: Icon | string); - name: string; - isEnabled: boolean; - /** - * Change item name - */ - setName(name: string): void; - /** - * Change item enabled state. - * Visually, item would has a tick next to it if its state is enabled. - */ - setState(isEnabled: boolean): void; - /** - * Change icon - */ - setIcon(icon: Icon | string): void; - /** - * Item is only available in Profile menu when method "register" is called. - */ - register(): void; - /** - * Stop item to be prepended into Profile menu. - */ - deregister(): void; - } - - /** - * Create a sub menu to contain Item toggles. - * `Item`s in `subItems` array shouldn't be registered. - */ - class SubMenu { - constructor(name: string, subItems: Item[]); - name: string; - /** - * Change SubMenu name - */ - setName(name: string): void; - /** - * Add an item to sub items list - */ - addItem(item: Item); - /** - * Remove an item from sub items list - */ - removeItem(item: Item); - /** - * SubMenu is only available in Profile menu when method "register" is called. - */ - register(): void; - /** - * Stop SubMenu to be prepended into Profile menu. - */ - deregister(): void; - } - } - - /** - * Keyboard shortcut library - * - * Documentation: https://craig.is/killing/mice v1.6.5 - * - * Spicetify.Keyboard is wrapper of this library to be compatible with legacy Spotify, - * so new extension should use this library instead. - */ - function Mousetrap(element?: any): void; - - /** - * Contains vast array of internal APIs. - * Please explore in Devtool Console. - */ - const Platform: any; - /** - * Queue object contains list of queuing tracks, - * history of played tracks and current track metadata. - */ - const Queue: { - nextTracks: any[]; - prevTracks: any[]; - queueRevision: string; - track: any; - }; - /** - * Remove a track or array of tracks from current queue. - */ - function removeFromQueue(uri: ContextTrack[]): Promise; - /** - * Display a bubble of notification. Useful for a visual feedback. - * @param message Message to display. Can use inline HTML for styling. - * @param isError If true, bubble will be red. Defaults to false. - * @param msTimeout Time in milliseconds to display the bubble. Defaults to Spotify's value. - */ - function showNotification(message: React.ReactNode, isError?: boolean, msTimeout?: number): void; - /** - * Set of APIs method to parse and validate URIs. - */ - class URI { - constructor(type: string, props: any); - public type: string; - public hasBase62Id: boolean; - - public id?: string; - public disc?: any; - public args?: any; - public category?: string; - public username?: string; - public track?: string; - public artist?: string; - public album?: string; - public duration?: number; - public query?: string; - public country?: string; - public global?: boolean; - public context?: string | typeof URI | null; - public anchor?: string; - public play?: any; - public toplist?: any; - - /** - * - * @return The URI representation of this uri. - */ - toURI(): string; - - /** - * - * @return The URI representation of this uri. - */ - toString(): string; - - /** - * Get the URL path of this uri. - * - * @param opt_leadingSlash True if a leading slash should be prepended. - * @return The path of this uri. - */ - toURLPath(opt_leadingSlash: boolean): string; - - /** - * - * @param origin The origin to use for the URL. - * @return The URL string for the uri. - */ - toURL(origin?: string): string; - - /** - * Clones a given SpotifyURI instance. - * - * @return An instance of URI. - */ - clone(): URI | null; - - /** - * Gets the path of the URI object by removing all hash and query parameters. - * - * @return The path of the URI object. - */ - getPath(): string; - - /** - * The various URI Types. - * - * Note that some of the types in this enum are not real URI types, but are - * actually URI particles. They are marked so. - * - */ - static Type: { - AD: string; - ALBUM: string; - GENRE: string; - QUEUE: string; - APPLICATION: string; - ARTIST: string; - ARTIST_TOPLIST: string; - ARTIST_CONCERTS: string; - AUDIO_FILE: string; - COLLECTION: string; - COLLECTION_ALBUM: string; - COLLECTION_ARTIST: string; - COLLECTION_MISSING_ALBUM: string; - COLLECTION_TRACK_LIST: string; - CONCERT: string; - CONTEXT_GROUP: string; - DAILY_MIX: string; - EMPTY: string; - EPISODE: string; - /** URI particle; not an actual URI. */ - FACEBOOK: string; - FOLDER: string; - FOLLOWERS: string; - FOLLOWING: string; - IMAGE: string; - INBOX: string; - INTERRUPTION: string; - LIBRARY: string; - LIVE: string; - ROOM: string; - EXPRESSION: string; - LOCAL: string; - LOCAL_TRACK: string; - LOCAL_ALBUM: string; - LOCAL_ARTIST: string; - MERCH: string; - MOSAIC: string; - PLAYLIST: string; - PLAYLIST_V2: string; - PRERELEASE: string; - PROFILE: string; - PUBLISHED_ROOTLIST: string; - RADIO: string; - ROOTLIST: string; - SEARCH: string; - SHOW: string; - SOCIAL_SESSION: string; - SPECIAL: string; - STARRED: string; - STATION: string; - TEMP_PLAYLIST: string; - TOPLIST: string; - TRACK: string; - TRACKSET: string; - USER_TOPLIST: string; - USER_TOP_TRACKS: string; - UNKNOWN: string; - MEDIA: string; - QUESTION: string; - POLL: string; - }; - - /** - * Creates a new URI object from a parsed string argument. - * - * @param str The string that will be parsed into a URI object. - * @throws TypeError If the string argument is not a valid URI, a TypeError will - * be thrown. - * @return The parsed URI object. - */ - static fromString(str: string): URI; - - /** - * Parses a given object into a URI instance. - * - * Unlike URI.fromString, this function could receive any kind of value. If - * the value is already a URI instance, it is simply returned. - * Otherwise the value will be stringified before parsing. - * - * This function also does not throw an error like URI.fromString, but - * instead simply returns null if it can't parse the value. - * - * @param value The value to parse. - * @return The corresponding URI instance, or null if the - * passed value is not a valid value. - */ - static from(value: any): URI | null; - - /** - * Checks whether two URI:s refer to the same thing even though they might - * not necessarily be equal. - * - * These two Playlist URIs, for example, refer to the same playlist: - * - * spotify:user:napstersean:playlist:3vxotOnOGDlZXyzJPLFnm2 - * spotify:playlist:3vxotOnOGDlZXyzJPLFnm2 - * - * @param baseUri The first URI to compare. - * @param refUri The second URI to compare. - * @return Whether they shared idenitity - */ - static isSameIdentity(baseUri: URI | string, refUri: URI | string): boolean; - - /** - * Returns the hex representation of a Base62 encoded id. - * - * @param id The base62 encoded id. - * @return The hex representation of the base62 id. - */ - static idToHex(id: string): string; - - /** - * Returns the base62 representation of a hex encoded id. - * - * @param hex The hex encoded id. - * @return The base62 representation of the id. - */ - static hexToId(hex: string): string; - - /** - * Creates a new 'album' type URI. - * - * @param id The id of the album. - * @param disc The disc number of the album. - * @return The album URI. - */ - static albumURI(id: string, disc: number): URI; - - /** - * Creates a new 'application' type URI. - * - * @param id The id of the application. - * @param args An array containing the arguments to the app. - * @return The application URI. - */ - static applicationURI(id: string, args: string[]): URI; - - /** - * Creates a new 'artist' type URI. - * - * @param id The id of the artist. - * @return The artist URI. - */ - static artistURI(id: string): URI; - - /** - * Creates a new 'collection' type URI. - * - * @param username The non-canonical username of the rootlist owner. - * @param category The category of the collection. - * @return The collection URI. - */ - static collectionURI(username: string, category: string): URI; - - /** - * Creates a new 'collection-album' type URI. - * - * @param username The non-canonical username of the rootlist owner. - * @param id The id of the album. - * @return The collection album URI. - */ - static collectionAlbumURI(username: string, id: string): URI; - - /** - * Creates a new 'collection-artist' type URI. - * - * @param username The non-canonical username of the rootlist owner. - * @param id The id of the artist. - * @return The collection artist URI. - */ - static collectionAlbumURI(username: string, id: string): URI; - - /** - * Creates a new 'concert' type URI. - * - * @param id The id of the concert. - * @return The concert URI. - */ - static concertURI(id: string): URI; - - /** - * Creates a new 'episode' type URI. - * - * @param id The id of the episode. - * @return The episode URI. - */ - static episodeURI(id: string): URI; - - /** - * Creates a new 'folder' type URI. - * - * @param id The id of the folder. - * @return The folder URI. - */ - static folderURI(id: string): URI; - - /** - * Creates a new 'local-album' type URI. - * - * @param artist The artist of the album. - * @param album The name of the album. - * @return The local album URI. - */ - static localAlbumURI(artist: string, album: string): URI; - - /** - * Creates a new 'local-artist' type URI. - * - * @param artist The name of the artist. - * @return The local artist URI. - */ - static localArtistURI(artist: string): URI; - - /** - * Creates a new 'playlist-v2' type URI. - * - * @param id The id of the playlist. - * @return The playlist URI. - */ - static playlistV2URI(id: string): URI; - - /** - * Creates a new 'prerelease' type URI. - * - * @param id The id of the prerelease. - * @return The prerelease URI. - */ - static prereleaseURI(id: string): URI; - - /** - * Creates a new 'profile' type URI. - * - * @param username The non-canonical username of the rootlist owner. - * @param args A list of arguments. - * @return The profile URI. - */ - static profileURI(username: string, args: string[]): URI; - - /** - * Creates a new 'search' type URI. - * - * @param query The unencoded search query. - * @return The search URI - */ - static searchURI(query: string): URI; - - /** - * Creates a new 'show' type URI. - * - * @param id The id of the show. - * @return The show URI. - */ - static showURI(id: string): URI; - - /** - * Creates a new 'station' type URI. - * - * @param args An array of arguments for the station. - * @return The station URI. - */ - static stationURI(args: string[]): URI; - - /** - * Creates a new 'track' type URI. - * - * @param id The id of the track. - * @param anchor The point in the track formatted as mm:ss - * @param context An optional context URI - * @param play Toggles autoplay - * @return The track URI. - */ - static trackURI(id: string, anchor: string, context?: string, play?: boolean): URI; - - /** - * Creates a new 'user-toplist' type URI. - * - * @param username The non-canonical username of the toplist owner. - * @param toplist The toplist type. - * @return The user-toplist URI. - */ - static userToplistURI(username: string, toplist: string): URI; - - static isAd(uri: URI | string): boolean; - static isAlbum(uri: URI | string): boolean; - static isGenre(uri: URI | string): boolean; - static isQueue(uri: URI | string): boolean; - static isApplication(uri: URI | string): boolean; - static isArtist(uri: URI | string): boolean; - static isArtistToplist(uri: URI | string): boolean; - static isArtistConcerts(uri: URI | string): boolean; - static isAudioFile(uri: URI | string): boolean; - static isCollection(uri: URI | string): boolean; - static isCollectionAlbum(uri: URI | string): boolean; - static isCollectionArtist(uri: URI | string): boolean; - static isCollectionMissingAlbum(uri: URI | string): boolean; - static isCollectionTrackList(uri: URI | string): boolean; - static isConcert(uri: URI | string): boolean; - static isContextGroup(uri: URI | string): boolean; - static isDailyMix(uri: URI | string): boolean; - static isEmpty(uri: URI | string): boolean; - static isEpisode(uri: URI | string): boolean; - static isFacebook(uri: URI | string): boolean; - static isFolder(uri: URI | string): boolean; - static isFollowers(uri: URI | string): boolean; - static isFollowing(uri: URI | string): boolean; - static isImage(uri: URI | string): boolean; - static isInbox(uri: URI | string): boolean; - static isInterruption(uri: URI | string): boolean; - static isLibrary(uri: URI | string): boolean; - static isLive(uri: URI | string): boolean; - static isRoom(uri: URI | string): boolean; - static isExpression(uri: URI | string): boolean; - static isLocal(uri: URI | string): boolean; - static isLocalTrack(uri: URI | string): boolean; - static isLocalAlbum(uri: URI | string): boolean; - static isLocalArtist(uri: URI | string): boolean; - static isMerch(uri: URI | string): boolean; - static isMosaic(uri: URI | string): boolean; - static isPlaylist(uri: URI | string): boolean; - static isPlaylistV2(uri: URI | string): boolean; - static isPrerelease(uri: URI | string): boolean; - static isProfile(uri: URI | string): boolean; - static isPublishedRootlist(uri: URI | string): boolean; - static isRadio(uri: URI | string): boolean; - static isRootlist(uri: URI | string): boolean; - static isSearch(uri: URI | string): boolean; - static isShow(uri: URI | string): boolean; - static isSocialSession(uri: URI | string): boolean; - static isSpecial(uri: URI | string): boolean; - static isStarred(uri: URI | string): boolean; - static isStation(uri: URI | string): boolean; - static isTempPlaylist(uri: URI | string): boolean; - static isToplist(uri: URI | string): boolean; - static isTrack(uri: URI | string): boolean; - static isTrackset(uri: URI | string): boolean; - static isUserToplist(uri: URI | string): boolean; - static isUserTopTracks(uri: URI | string): boolean; - static isUnknown(uri: URI | string): boolean; - static isMedia(uri: URI | string): boolean; - static isQuestion(uri: URI | string): boolean; - static isPoll(uri: URI | string): boolean; - static isPlaylistV1OrV2(uri: URI | string): boolean; - } - - /** - * Create custom menu item and prepend to right click context menu - */ - namespace ContextMenu { - type OnClickCallback = (uris: string[], uids?: string[], contextUri?: string) => void; - type ShouldAddCallback = (uris: string[], uids?: string[], contextUri?: string) => boolean; - - // Single context menu item - class Item { - /** - * List of valid icons to use. - */ - static readonly iconList: Icon[]; - constructor(name: string, onClick: OnClickCallback, shouldAdd?: ShouldAddCallback, icon?: Icon, disabled?: boolean); - name: string; - icon: Icon | string; - disabled: boolean; - /** - * A function returning boolean determines whether item should be prepended. - */ - shouldAdd: ShouldAddCallback; - /** - * A function to call when item is clicked - */ - onClick: OnClickCallback; - /** - * Item is only available in Context Menu when method "register" is called. - */ - register: () => void; - /** - * Stop Item to be prepended into Context Menu. - */ - deregister: () => void; - } - - /** - * Create a sub menu to contain `Item`s. - * `Item`s in `subItems` array shouldn't be registered. - */ - class SubMenu { - constructor(name: string, subItems: Iterable, shouldAdd?: ShouldAddCallback, disabled?: boolean); - name: string; - disabled: boolean; - /** - * A function returning boolean determines whether item should be prepended. - */ - shouldAdd: ShouldAddCallback; - addItem: (item: Item) => void; - removeItem: (item: Item) => void; - /** - * SubMenu is only available in Context Menu when method "register" is called. - */ - register: () => void; - /** - * Stop SubMenu to be prepended into Context Menu. - */ - deregister: () => void; - } - } - - /** - * Popup Modal - */ - namespace PopupModal { - interface Content { - title: string; - /** - * You can specify a string for simple text display - * or a HTML element for interactive config/setting menu - */ - content: string | Element; - /** - * Bigger window - */ - isLarge?: boolean; - } - - function display(e: Content): void; - function hide(): void; - } - - /** React instance to create components */ - const React: any; - /** React DOM instance to render and mount components */ - const ReactDOM: any; - /** React DOM Server instance to render components to string */ - const ReactDOMServer: any; - - /** Stock React components exposed from Spotify library */ - namespace ReactComponent { - type ContextMenuProps = { - /** - * Decide whether to use the global singleton context menu (rendered in ) - * or a new inline context menu (rendered in a sibling - * element to `children`) - */ - renderInline?: boolean; - /** - * Determins what will trigger the context menu. For example, a click, or a right-click - */ - trigger?: "click" | "right-click"; - /** - * Determins is the context menu should open or toggle when triggered - */ - action?: "toggle" | "open"; - /** - * The preferred placement of the context menu when it opens. - * Relative to trigger element. - */ - placement?: - | "top" - | "top-start" - | "top-end" - | "right" - | "right-start" - | "right-end" - | "bottom" - | "bottom-start" - | "bottom-end" - | "left" - | "left-start" - | "left-end"; - /** - * The x and y offset distances at which the context menu should open. - * Relative to trigger element and `position`. - */ - offset?: [number, number]; - /** - * Will stop the client from scrolling while the context menu is open - */ - preventScrollingWhileOpen?: boolean; - /** - * The menu UI to render inside of the context menu. - */ - menu: - | typeof Spicetify.ReactComponent.Menu - | typeof Spicetify.ReactComponent.AlbumMenu - | typeof Spicetify.ReactComponent.PodcastShowMenu - | typeof Spicetify.ReactComponent.ArtistMenu - | typeof Spicetify.ReactComponent.PlaylistMenu; - /** - * A child of the context menu. Should be `