diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d6e816..4800873 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: - name: Install Rust uses: hecrj/setup-rust-action@v2 with: - rust-version: nightly + rust-version: nightly-2025-02-17 components: rust-src targets: wasm32-unknown-unknown @@ -114,7 +114,7 @@ jobs: mv wasm-bindgen-${{ steps.wasm-bindgen.outputs.version }}-${{ matrix.target }}/wasm* . - name: Install npm packages - run: npm ci -f + run: npm ci env: DETECT_CHROMEDRIVER_VERSION: true diff --git a/README.md b/README.md index db72aa0..01dd25b 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ cargo install wasm-bindgen-cli You need to set up some npm modules before compiling the project: ```bash -npm install -f +npm install ``` You are now ready to build livesplit-core, which powers LiveSplit One: diff --git a/buildCore.js b/buildCore.js index c027053..fd1fa8e 100644 --- a/buildCore.js +++ b/buildCore.js @@ -28,11 +28,17 @@ if (process.argv.some((v) => v === "--max-opt")) { if (process.argv.some((v) => v === "--unstable")) { // Relaxed SIMD is not supported by Firefox and Safari yet. rustFlags += ",+relaxed-simd"; + + // Not supported by any browser yet. + rustFlags += ",+wide-arithmetic"; } // Use the nightly toolchain, which enables some more optimizations. if (process.argv.some((v) => v === "--nightly")) { - toolchain = "+nightly"; + // FIXME: Nightly is broken since the LLVM 20 upgrade. We need to wait for + // stdarch (which has the fix already) to be updated: + // https://github.com/rust-lang/stdarch/pull/1719 + toolchain = "+nightly-2025-02-17"; cargoFlags += " -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort"; rustFlags += " -Z wasm-c-abi=spec"; diff --git a/package-lock.json b/package-lock.json index 7119e7b..b066c45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,38 +8,39 @@ "dependencies": { "idb": "^8.0.0", "markdown-it": "^14.1.0", - "react": "^18.3.1", - "react-contextmenu": "^2.14.0", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-resizable": "^3.0.4", "react-sidebar": "^3.0.2", - "react-toastify": "11.0.3" + "react-toastify": "^11.0.3" }, "devDependencies": { + "@babel/plugin-proposal-explicit-resource-management": "^7.25.9", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.13", "@tauri-apps/cli": "^1.6.0", "@types/markdown-it": "^14.1.2", - "@types/node": "22.13.1", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", + "@types/node": "^22.13.1", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "@types/react-resizable": "^3.0.3", "@types/react-sidebar": "^3.0.2", "clean-webpack-plugin": "^4.0.0", - "css-loader": "7.1.2", + "css-loader": "^7.1.2", "eslint": "^9.20.1", "favicons-webpack-plugin": "^6.0.0", "html-inline-script-webpack-plugin": "^3.1.0", "html-webpack-plugin": "^5.6.0", + "react-compiler-webpack": "^0.1.2", "react-refresh-typescript": "^2.0.7", "sass": "^1.77.2", - "sass-loader": "16.0.4", + "sass-loader": "^16.0.4", "style-loader": "^4.0.0", "ts-loader": "^9.4.2", "typescript": "^5.7.3", "typescript-eslint": "^8.24.0", "webpack": "^5.91.0", - "webpack-cli": "6.0.1", - "webpack-dev-server": "5.2.0", + "webpack-cli": "^6.0.1", + "webpack-dev-server": "^5.2.0", "workbox-webpack-plugin": "^7.0.0" } }, @@ -535,6 +536,22 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-proposal-explicit-resource-management": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-explicit-resource-management/-/plugin-proposal-explicit-resource-management-7.25.9.tgz", + "integrity": "sha512-EbtfSvb6s4lZwef1nH52nw4DTUAvHY6bl1mbLgEHUkR6L8j4WY70mM/InB8Ozgdlok/7etbiSW+Wc88ZebZAKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -580,6 +597,38 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-unicode-sets-regex": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", @@ -1760,32 +1809,19 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.6.tgz", + "integrity": "sha512-+0TjwR1eAUdZtvv/ir1mGX+v0tUoR3VEPB8Up0LLJC+whRW0GgBBtpbOkg/a/U4Dxa6l5a3l9AJ1aWIQVyoWJA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.11.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1839,9 +1875,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3337,9 +3373,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", - "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "version": "22.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", "dev": true, "license": "MIT", "dependencies": { @@ -3356,13 +3392,6 @@ "@types/node": "*" } }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", @@ -3378,24 +3407,23 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", - "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", "dev": true, "license": "MIT", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", + "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^18.0.0" + "@types/react": "^19.0.0" } }, "node_modules/@types/react-resizable": { @@ -3493,17 +3521,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.0.tgz", - "integrity": "sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz", + "integrity": "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.24.0", - "@typescript-eslint/type-utils": "8.24.0", - "@typescript-eslint/utils": "8.24.0", - "@typescript-eslint/visitor-keys": "8.24.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/type-utils": "8.24.1", + "@typescript-eslint/utils": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3523,16 +3551,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.0.tgz", - "integrity": "sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.1.tgz", + "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.24.0", - "@typescript-eslint/types": "8.24.0", - "@typescript-eslint/typescript-estree": "8.24.0", - "@typescript-eslint/visitor-keys": "8.24.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4" }, "engines": { @@ -3548,14 +3576,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz", - "integrity": "sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz", + "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.24.0", - "@typescript-eslint/visitor-keys": "8.24.0" + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3566,14 +3594,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.0.tgz", - "integrity": "sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.1.tgz", + "integrity": "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.24.0", - "@typescript-eslint/utils": "8.24.0", + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/utils": "8.24.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -3590,9 +3618,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.0.tgz", - "integrity": "sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz", + "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==", "dev": true, "license": "MIT", "engines": { @@ -3604,14 +3632,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz", - "integrity": "sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz", + "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.24.0", - "@typescript-eslint/visitor-keys": "8.24.0", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3657,16 +3685,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.0.tgz", - "integrity": "sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz", + "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.24.0", - "@typescript-eslint/types": "8.24.0", - "@typescript-eslint/typescript-estree": "8.24.0" + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3681,13 +3709,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz", - "integrity": "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz", + "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/types": "8.24.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -4272,6 +4300,17 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/babel-plugin-react-compiler": { + "version": "19.0.0-beta-21e868a-20250216", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-21e868a-20250216.tgz", + "integrity": "sha512-WDOBsm9t9P0RADm8CSlav5OqWvs+3mZFvrBo/qf3vuNtdz78OG5TFxOy7De8ePR3rA6qg1Qmcjjae6nR1pOpCA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.19.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4531,9 +4570,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001699", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz", - "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==", + "version": "1.0.30001700", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", + "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", "dev": true, "funding": [ { @@ -4594,12 +4633,6 @@ "node": ">=6.0" } }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" - }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -4655,9 +4688,9 @@ } }, "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", "license": "MIT", "engines": { "node": ">=6" @@ -5365,9 +5398,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.101", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.101.tgz", - "integrity": "sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA==", + "version": "1.5.102", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz", + "integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==", "dev": true, "license": "ISC" }, @@ -6145,9 +6178,9 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, @@ -7762,9 +7795,9 @@ } }, "node_modules/launch-editor": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", - "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz", + "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==", "dev": true, "license": "MIT", "dependencies": { @@ -8682,9 +8715,9 @@ } }, "node_modules/postcss": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", - "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, "funding": [ { @@ -8963,43 +8996,64 @@ } }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/react-contextmenu": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/react-contextmenu/-/react-contextmenu-2.14.0.tgz", - "integrity": "sha512-ktqMOuad6sCFNJs/ltEwppN8F0YeXmqoZfwycgtZR/MxOXMYx1xgYC44SzWH259HdGyshk1/7sXGuIRwj9hzbw==", + "node_modules/react-compiler-webpack": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/react-compiler-webpack/-/react-compiler-webpack-0.1.2.tgz", + "integrity": "sha512-NaT5Ft4FRqiDAllr8ywjmXWcv0+ouL/4GsYiLHj783C2HbTp4SGUtUC/6vpWT/UQ5LggV+PUMLUrdEdua+LOaQ==", + "dev": true, "license": "MIT", "dependencies": { - "classnames": "^2.2.5", - "object-assign": "^4.1.0" + "@babel/core": "^7.24.6", + "@babel/plugin-syntax-jsx": "^7.24.6", + "@babel/plugin-syntax-typescript": "^7.24.6", + "loader-utils": "^3.2.2" }, "peerDependencies": { - "prop-types": "^15.0.0", - "react": "^0.14.0 || ^15.0.0 || ^16.0.1", - "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.1" + "babel-plugin-react-compiler": "*" + } + }, + "node_modules/react-compiler-webpack/node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.25.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.0.0" + } + }, + "node_modules/react-draggable": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", + "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", + "license": "MIT", + "dependencies": { + "clsx": "^1.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" } }, "node_modules/react-is": { @@ -9043,29 +9097,6 @@ "react": ">= 16.3" } }, - "node_modules/react-resizable/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/react-resizable/node_modules/react-draggable": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", - "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", - "license": "MIT", - "dependencies": { - "clsx": "^1.1.1", - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "react": ">= 16.3.0", - "react-dom": ">= 16.3.0" - } - }, "node_modules/react-sidebar": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/react-sidebar/-/react-sidebar-3.0.2.tgz", @@ -9093,6 +9124,15 @@ "react-dom": "^18 || ^19" } }, + "node_modules/react-toastify/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -9554,9 +9594,9 @@ } }, "node_modules/sass-loader": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.4.tgz", - "integrity": "sha512-LavLbgbBGUt3wCiYzhuLLu65+fWXaXLmq7YxivLhEqmiupCFZ5sKUAipK3do6V80YSU0jvSxNhEdT13IXNr3rg==", + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.5.tgz", + "integrity": "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==", "dev": true, "license": "MIT", "dependencies": { @@ -9603,13 +9643,10 @@ "peer": true }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" }, "node_modules/schema-utils": { "version": "4.3.0", @@ -10663,9 +10700,9 @@ } }, "node_modules/type-fest": { - "version": "4.34.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.34.1.tgz", - "integrity": "sha512-6kSc32kT0rbwxD6QL1CYe8IqdzN/J/ILMrNK+HMQCKH3insCDRY/3ITb0vcBss0a3t72fzh2YSzj8ko1HgwT3g==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.35.0.tgz", + "integrity": "sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==", "dev": true, "license": "(MIT OR CC0-1.0)", "optional": true, @@ -10784,15 +10821,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.24.0.tgz", - "integrity": "sha512-/lmv4366en/qbB32Vz5+kCNZEMf6xYHwh1z48suBwZvAtnXKbP+YhGe8OLE2BqC67LMqKkCNLtjejdwsdW6uOQ==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.24.1.tgz", + "integrity": "sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.24.0", - "@typescript-eslint/parser": "8.24.0", - "@typescript-eslint/utils": "8.24.0" + "@typescript-eslint/eslint-plugin": "8.24.1", + "@typescript-eslint/parser": "8.24.1", + "@typescript-eslint/utils": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index a55e6e2..6e485dd 100644 --- a/package.json +++ b/package.json @@ -17,38 +17,39 @@ "dependencies": { "idb": "^8.0.0", "markdown-it": "^14.1.0", - "react": "^18.3.1", - "react-contextmenu": "^2.14.0", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-resizable": "^3.0.4", "react-sidebar": "^3.0.2", - "react-toastify": "11.0.3" + "react-toastify": "^11.0.3" }, "devDependencies": { + "@babel/plugin-proposal-explicit-resource-management": "^7.25.9", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.13", "@tauri-apps/cli": "^1.6.0", "@types/markdown-it": "^14.1.2", - "@types/node": "22.13.1", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", + "@types/node": "^22.13.1", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "@types/react-resizable": "^3.0.3", "@types/react-sidebar": "^3.0.2", "clean-webpack-plugin": "^4.0.0", - "css-loader": "7.1.2", + "css-loader": "^7.1.2", "eslint": "^9.20.1", "favicons-webpack-plugin": "^6.0.0", "html-inline-script-webpack-plugin": "^3.1.0", "html-webpack-plugin": "^5.6.0", + "react-compiler-webpack": "^0.1.2", "react-refresh-typescript": "^2.0.7", "sass": "^1.77.2", - "sass-loader": "16.0.4", + "sass-loader": "^16.0.4", "style-loader": "^4.0.0", "ts-loader": "^9.4.2", "typescript": "^5.7.3", "typescript-eslint": "^8.24.0", "webpack": "^5.91.0", - "webpack-cli": "6.0.1", - "webpack-dev-server": "5.2.0", + "webpack-cli": "^6.0.1", + "webpack-dev-server": "^5.2.0", "workbox-webpack-plugin": "^7.0.0" } } diff --git a/src/css/About.scss b/src/css/About.scss index d8f04a5..2e04ebf 100644 --- a/src/css/About.scss +++ b/src/css/About.scss @@ -1,5 +1,5 @@ @use 'mobile'; -@use 'variables'; +@use 'variables.icss'; $icon-size: 40px; $title-font-size: 40px; diff --git a/src/css/ColorPicker.module.scss b/src/css/ColorPicker.module.scss new file mode 100644 index 0000000..ca90d7a --- /dev/null +++ b/src/css/ColorPicker.module.scss @@ -0,0 +1,225 @@ +@use 'variables.icss'; + +.colorPickerButton { + border: 2px solid white; + border-radius: 2px; + box-shadow: 0 0 0 1px rgba(0,0,0,.1); + cursor: pointer; + box-sizing: border-box; + height: variables.$settings-row-height; +} + +.colorPickerDialogPositioning { + margin: 0 auto; + width: 0; +} + +.overlay { + inset: 0; + position: fixed; + z-index: 1; +} + +.glassPanel { + background-color: rgba(28, 28, 28, 0.8); + backdrop-filter: blur(5px); + z-index: 1; + position: absolute; + margin-top: 5px; + margin-left: -113.5px; + border: 1px solid rgba(255, 255, 255, 0.25); + box-shadow: 0 5px 10px 0px rgba(28, 28, 28, 0.8); +} + +.hr { + margin: 0; + height: 1px; + border-width: 0px; + background: rgba(255, 255, 255, 0.25); +} + +.gradientSelector { + overflow: hidden; + position: relative; + + .whiteGradient { + background: linear-gradient(to right, white, transparent); + + .cursor { + pointer-events: none; + position: absolute; + width: 12px; + height: 12px; + border-radius: 6px; + box-shadow: black 0 0 0 2px inset; + + >div { + width: 100%; + height: 100%; + border-radius: 6px; + box-shadow: white 0 0 0 1px inset; + } + } + + .blackGradient { + width: 225px; + height: 125px; + background: linear-gradient(to top, black, transparent); + } + } +} + +.controlPanel { + margin: 10px; + display: flex; + flex-direction: column; + gap: 10px; + + .controlPanelTop { + display: flex; + gap: 10px; + align-items: center; + + .checker { + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAADFJREFUOE9jZGBgEGHAD97gk2YcNYBhmIQBgWSAP52AwoAQwJvQRg1gACckQoC2gQgAIF8IscwEtKYAAAAASUVORK5CYII=") left center; + } + + .sliders { + flex-grow: 1; + display: flex; + flex-direction: column; + gap: 10px; + + .hueSlider { + background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); + height: 18px; + position: relative; + border-radius: 9px; + + input { + margin: 0; + background: none; + height: 16px; + position: absolute; + top: 1px; + left: 1px; + } + } + + .alphaSlider { + background: white; + border-radius: 9px; + overflow: hidden; + height: 18px; + position: relative; + + input { + margin: 0; + background: none; + height: 16px; + position: absolute; + top: 1px; + left: 1px; + } + } + } + } + + .controlPanelBottom { + display: flex; + gap: 10px; + cursor: ew-resize; + } + + input[type="range"] { + /* Hack for LSO to prevent global style */ + all: revert; + appearance: none; + accent-color: white; + width: calc(100% - 2px); + } +} + +.colorPreview { + border: 1px solid rgba(255, 255, 255, 0.25); + border-radius: 50%; + background: white; + position: relative; + overflow: hidden; + + .colorPreviewInner { + width: 35px; + height: 35px; + display: flex; + justify-content: center; + align-items: center; + } +} + +.colorInput { + display: flex; + flex-direction: column; + gap: 5px; + font-size: 12px; + color: rgba(255, 255, 255, 0.7); + align-items: center; + flex: 1; + width: 0; + user-select: none; + + >input { + width: 100%; + /* Hack for LSO to prevent global input height style */ + height: 25px !important; + font-family: inherit; + font-size: 14px; + text-align: center; + color: white; + font-variant-numeric: tabular-nums; + background: transparent; + border: 1px solid rgba(255, 255, 255, 0.25); + border-radius: 5px; + padding: 0; + } +} + +.predefinedColors { + margin: 10px; + display: flex; + flex-direction: column; + gap: 10px; + + .predefinedColorsRow { + display: flex; + justify-content: space-between; + } +} + +.predefinedColor { + /* Hack for LSO to prevent global button style */ + all: revert; + border: 3px solid transparent; + border-radius: 50%; + width: 20px; + height: 20px; + cursor: pointer; + transition: all .05s, border-color .25s; + /* Fix for iOS minimum button width */ + font-size: 0; + + &:hover { + /* Hack for LSO to prevent global button style */ + background: revert; + border: 3px solid white; + } + + &:active { + margin-top: 3px; + height: 17px; + } + + &:active:hover { + /* Hack for LSO to prevent global button style */ + background: revert; + } +} diff --git a/src/css/ColorPicker.scss b/src/css/ColorPicker.scss deleted file mode 100644 index c98b364..0000000 --- a/src/css/ColorPicker.scss +++ /dev/null @@ -1,88 +0,0 @@ -@use 'variables'; - -.color-picker-button { - border: 2px solid white; - border-radius: 2px; - box-shadow: 0 0 0 1px rgba(0,0,0,.1); - cursor: pointer; - box-sizing: border-box; - height: variables.$settings-row-height; -} - -.color-picker { - .blur { - background-color: rgba(28, 28, 28, 0.8); - backdrop-filter: blur(5px); - } - - input[type="range"] { - /* Hack for LSO to prevent global style */ - all: revert; - appearance: none; - accent-color: white; - width: calc(100% - 2px); - } - - .predefined-color { - /* Hack for LSO to prevent global button style */ - all: revert; - border: 3px solid transparent; - border-radius: 50%; - width: 20px; - height: 20px; - cursor: pointer; - transition: all .05s, border-color .25s; - /* Fix for iOS minimum button width */ - font-size: 0; - - &:hover { - border: 3px solid white; - } - - &:active { - margin-top: 3px; - height: 17px; - } - - &:active:hover { - /* Hack for LSO to prevent global button style */ - background: revert; - } - } - - .color-input { - display: flex; - flex-direction: column; - gap: 5px; - font-size: 12px; - color: rgba(255, 255, 255, 0.7); - align-items: center; - flex: 1; - width: 0; - user-select: none; - - >input { - width: 100%; - /* Hack for LSO to prevent global input height style */ - height: 25px !important; - font-size: 14px; - text-align: center; - color: white; - font-variant-numeric: tabular-nums; - background: transparent; - border: 1px solid rgba(255, 255, 255, 0.25); - border-radius: 5px; - padding: 0; - } - } - - .checker { - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAADFJREFUOE9jZGBgEGHAD97gk2YcNYBhmIQBgWSAP52AwoAQwJvQRg1gACckQoC2gQgAIF8IscwEtKYAAAAASUVORK5CYII=") left center; - } - - .overlay { - inset: 0; - position: fixed; - z-index: 1; - } -} diff --git a/src/css/ContextMenu.module.css b/src/css/ContextMenu.module.css new file mode 100644 index 0000000..e7e9981 --- /dev/null +++ b/src/css/ContextMenu.module.css @@ -0,0 +1,45 @@ +.contextMenu { + position: fixed; + z-index: 3; +} + +.panel { + z-index: 3; + background-color: rgba(28, 28, 28, .8); + backdrop-filter: blur(5px); + min-width: 100px; + padding: 4px 0; + border: 1px solid rgba(255, 255, 255, .25); + border-radius: 9px; + box-shadow: 0 5px 10px 0px rgba(28, 28, 28, .8); + display: flex; + flex-direction: column; + user-select: none; + text-align: left; + font-weight: initial; +} + +.overlay { + inset: 0; + position: fixed; +} + +.entry { + padding: 4px; + cursor: pointer; + font-size: 16px; + transition: background-color .25s; + border-radius: 5px; + margin: 0 4px; + + &:hover { + background-color: rgba(255, 255, 255, .25); + } +} + +.hr { + height: 1px; + border-width: 0px; + background: rgba(255, 255, 255, .25); + margin: 4px 0; +} diff --git a/src/css/ContextMenu.scss b/src/css/ContextMenu.scss deleted file mode 100644 index 2881e57..0000000 --- a/src/css/ContextMenu.scss +++ /dev/null @@ -1,48 +0,0 @@ -@use 'sass:math'; - -@use 'variables'; - -@mixin context-menu { - .react-contextmenu { - min-width: 160px; - padding: math.div(variables.$ui-margin, 2) 0; - margin: 2px 0 0; - font-size: 16px; - text-align: left; - background-color: variables.$dark-row-color; - background-clip: padding-box; - border: 1px solid variables.$border-color; - border-radius: 0.25rem; - outline: none; - opacity: 0; - pointer-events: none; - transition: opacity 250ms ease !important; - z-index: 3; - - &.react-contextmenu--visible { - opacity: 1; - pointer-events: auto; - } - - .react-contextmenu-item { - padding: math.div(variables.$ui-margin, 2) variables.$ui-margin; - font-weight: 400; - text-align: inherit; - white-space: nowrap; - background: 0 0; - border: 0; - cursor: pointer; - } - - .react-contextmenu-item--divider { - margin-bottom: math.div(variables.$ui-margin, 2); - padding: math.div(variables.$ui-margin, 2) 0 0 0; - border-bottom: 1px solid variables.$border-color; - cursor: inherit; - } - - .react-contextmenu-item--selected { - background: variables.$selected-row-color; - } - } -} diff --git a/src/css/Dialog.scss b/src/css/Dialog.scss index 0303dd4..32c93dc 100644 --- a/src/css/Dialog.scss +++ b/src/css/Dialog.scss @@ -1,4 +1,4 @@ -@use 'variables'; +@use 'variables.icss'; .is-mobile+dialog>.dialog { min-width: auto; @@ -15,8 +15,9 @@ dialog { .dialog { color: #eee; - background: variables.$main-background-color; - border: 2px solid variables.$border-color; + background-color: rgba(28, 28, 28, .8); + backdrop-filter: blur(5px); + border: 2px solid rgba(255, 255, 255, .25); border-radius: 10px; min-width: 225px; @@ -50,7 +51,7 @@ dialog { input { width: 100%; border: none; - border-bottom: 1px solid hsla(0, 0%, 100%, 0.25); + border-bottom: 1px solid rgba(255, 255, 255, .25); background: transparent; color: white; text-overflow: ellipsis; diff --git a/src/css/DragUpload.scss b/src/css/DragUpload.scss index 6387aea..bf60f3f 100644 --- a/src/css/DragUpload.scss +++ b/src/css/DragUpload.scss @@ -1,5 +1,5 @@ @use 'mobile'; -@use 'variables'; +@use 'variables.icss'; #upload-drop-zone { position: absolute; diff --git a/src/css/HotkeyButton.scss b/src/css/HotkeyButton.scss index a43e1fb..f495423 100644 --- a/src/css/HotkeyButton.scss +++ b/src/css/HotkeyButton.scss @@ -1,4 +1,4 @@ -@use 'variables'; +@use 'variables.icss'; .hotkey-box { button { diff --git a/src/css/LayoutEditor.scss b/src/css/LayoutEditor.scss index 7a2bdcb..c0d4000 100644 --- a/src/css/LayoutEditor.scss +++ b/src/css/LayoutEditor.scss @@ -1,13 +1,11 @@ @use 'mobile'; -@use 'ContextMenu'; @use 'Table'; @use 'Toggle'; -@use "variables"; +@use "variables.icss"; .layout-editor-outer { display: inline-flex; - @include ContextMenu.context-menu; @include Table.table; @include Toggle.toggle; diff --git a/src/css/LiveSplit.scss b/src/css/LiveSplit.scss index d0b7a6c..2a65e71 100644 --- a/src/css/LiveSplit.scss +++ b/src/css/LiveSplit.scss @@ -1,6 +1,6 @@ @use 'mobile'; @use 'Sidebar'; -@use 'variables'; +@use 'variables.icss'; $sidebar-button-height: 42px; diff --git a/src/css/LiveSplitServerButton.scss b/src/css/LiveSplitServerButton.scss index 76fc157..4daa6ba 100644 --- a/src/css/LiveSplitServerButton.scss +++ b/src/css/LiveSplitServerButton.scss @@ -1,4 +1,4 @@ -@use 'variables'; +@use 'variables.icss'; .livesplit-server-button { margin: 0; diff --git a/src/css/Markdown.scss b/src/css/Markdown.scss index 31465c8..1c9d42a 100644 --- a/src/css/Markdown.scss +++ b/src/css/Markdown.scss @@ -1,4 +1,4 @@ -@use 'variables'; +@use 'variables.icss'; @mixin markdown { .flag { diff --git a/src/css/RunEditor.scss b/src/css/RunEditor.scss index 478565e..e107478 100644 --- a/src/css/RunEditor.scss +++ b/src/css/RunEditor.scss @@ -1,11 +1,10 @@ @use 'sass:math'; -@use "ContextMenu"; @use "Markdown"; @use "mobile"; @use "Table"; @use "Toggle"; -@use "variables"; +@use "variables.icss"; $tab-bar-height: 30px + math.div(variables.$ui-margin, 2); $tab-width: 625px; @@ -18,7 +17,6 @@ $small-font-size: 13px; $small-button-padding: 1px 3px 1px 3px; .run-editor { - @include ContextMenu.context-menu; @include Markdown.markdown; @include Table.table; @include Toggle.toggle; @@ -205,18 +203,10 @@ $small-button-padding: 1px 3px 1px 3px; } &.segment-icon-container { - width: $segment-icon-size; + height: $segment-icon-size; cursor: pointer; - - div { - height: $segment-icon-size; - - .segment-icon { - width: 100%; - height: 100%; - object-fit: contain; - } - } + display: flex; + justify-content: center; } } diff --git a/src/css/Sidebar.scss b/src/css/Sidebar.scss index 159feb1..bef546b 100644 --- a/src/css/Sidebar.scss +++ b/src/css/Sidebar.scss @@ -1,7 +1,7 @@ @use 'sass:math'; @use 'Toggle'; -@use 'variables'; +@use 'variables.icss'; $h1-font-size: 24px; $h2-font-size: 22px; diff --git a/src/css/SplitsSelection.scss b/src/css/SplitsSelection.scss index 3ea049a..1c0d6b9 100644 --- a/src/css/SplitsSelection.scss +++ b/src/css/SplitsSelection.scss @@ -1,9 +1,8 @@ @use 'sass:math'; @use 'mobile'; -@use 'variables'; +@use 'variables.icss'; @use 'Table'; -@use 'ContextMenu'; $loading-text-font-size: 40px; $splits-row-width: 500px; @@ -86,8 +85,6 @@ $splits-row-height: 40px; flex-shrink: 0; margin-left: variables.$ui-large-margin; - @include ContextMenu.context-menu; - button { background: transparent; border: 0; diff --git a/src/css/Switch.scss b/src/css/Switch.module.scss similarity index 89% rename from src/css/Switch.scss rename to src/css/Switch.module.scss index a39382f..e96d00d 100644 --- a/src/css/Switch.scss +++ b/src/css/Switch.module.scss @@ -1,3 +1,10 @@ +.label { + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; +} + .switch { position: relative; width: 35px; diff --git a/src/css/Table.scss b/src/css/Table.scss index f3e500d..827ef6e 100644 --- a/src/css/Table.scss +++ b/src/css/Table.scss @@ -1,7 +1,7 @@ @use 'sass:math'; @use 'mobile'; -@use 'variables'; +@use 'variables.icss'; @mixin table { .table { @@ -74,14 +74,14 @@ } } - input { - font-size: 15px; - border: none; - border-bottom: 1px solid hsla(0, 0%, 100%, 0.25); + input.text-box { + font-family: inherit; background: transparent; color: white; text-overflow: ellipsis; - font-family: "fira", sans-serif; + font-size: 15px; + border: none; + border-bottom: 1px solid hsla(0, 0%, 100%, 0.25); } input[Type="text"] { @@ -94,7 +94,7 @@ border: 1px solid variables.$border-color; color: white; text-overflow: ellipsis; - font-family: "fira", sans-serif; + font-family: inherit; } .settings-table { diff --git a/src/css/TimerView.scss b/src/css/TimerView.scss index 76345e9..574997e 100644 --- a/src/css/TimerView.scss +++ b/src/css/TimerView.scss @@ -1,7 +1,7 @@ @use 'sass:math'; @use 'mobile'; -@use 'variables'; +@use 'variables.icss'; .buttons { margin-top: variables.$ui-large-margin - math.div(variables.$ui-margin, 2); diff --git a/src/css/Toggle.scss b/src/css/Toggle.scss index 7486e73..d139046 100644 --- a/src/css/Toggle.scss +++ b/src/css/Toggle.scss @@ -1,4 +1,4 @@ -@use 'variables'; +@use 'variables.icss'; @mixin toggle { .toggle-left { diff --git a/src/css/Tooltip.scss b/src/css/Tooltip.scss index 35e8724..b1ea6e4 100644 --- a/src/css/Tooltip.scss +++ b/src/css/Tooltip.scss @@ -1,5 +1,5 @@ @use 'mobile'; -@use 'variables'; +@use 'variables.icss'; .tooltip { position: relative; @@ -8,8 +8,9 @@ .tooltip .tooltip-text { visibility: hidden; width: 300px; - background-color: variables.$sidebar-background-color; - border: 1px solid variables.$border-color; + background-color: rgba(28, 28, 28, .8); + backdrop-filter: blur(5px); + border: 1px solid rgba(255, 255, 255, .25); color: #fff; text-align: center; border-radius: 6px; @@ -31,7 +32,7 @@ } } -.react-contextmenu-item.tooltip .tooltip-text { +.contextmenu-item.tooltip .tooltip-text { bottom: initial; left: calc(100% + variables.$ui-margin); margin-left: 0; diff --git a/src/css/main.scss b/src/css/main.scss index c35d42b..a8aadfd 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -1,7 +1,7 @@ @use 'sass:math'; @use 'browser_source'; -@use 'variables'; +@use 'variables.icss'; @font-face { font-family: timer; @@ -25,8 +25,9 @@ body { .Toastify { .toast-class { font-family: "fira", sans-serif; - background: variables.$sidebar-background-color !important; - border: 2px solid variables.$border-color !important; + background: rgba(28, 28, 28, .8) !important; + backdrop-filter: blur(5px); + border: 2px solid rgba(255, 255, 255, .25) !important; border-radius: 10px !important; min-height: 48px !important; diff --git a/src/css/variables.scss b/src/css/variables.icss.scss similarity index 100% rename from src/css/variables.scss rename to src/css/variables.icss.scss diff --git a/src/layout/Layout.tsx b/src/layout/Layout.tsx index 4562acb..a95308b 100644 --- a/src/layout/Layout.tsx +++ b/src/layout/Layout.tsx @@ -36,7 +36,7 @@ export default class Layout extends React.Component {
element?.appendChild(this.props.renderer.element())} + ref={(element) => { element?.appendChild(this.props.renderer.element()); }} /> { this.props.allowResize &&
diff --git a/src/type-definitions/commonmark-react-renderer.d.ts b/src/type-definitions/commonmark-react-renderer.d.ts deleted file mode 100644 index 99e573e..0000000 --- a/src/type-definitions/commonmark-react-renderer.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -declare module "commonmark-react-renderer" { - class ReactRenderer { - constructor(options?: Options); - render(ast: any): JSX.Element; - } - interface Options { - escapeHtml?: boolean, - linkTarget?: string, - softBreak?: string, - } - export = ReactRenderer; -} diff --git a/src/type-definitions/emojilib.d.ts b/src/type-definitions/emojilib.d.ts deleted file mode 100644 index d0ba9a0..0000000 --- a/src/type-definitions/emojilib.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -declare module "emojilib" { - export const lib: { - [emoji_name: string]: Emoji; - } - - export interface Emoji { - char: string, - keywords: string[], - } -} diff --git a/src/type-definitions/react-contextmenu.d.ts b/src/type-definitions/react-contextmenu.d.ts deleted file mode 100644 index d83c5d6..0000000 --- a/src/type-definitions/react-contextmenu.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -declare module "react-contextmenu" { - import { ReactNode } from "react"; - - export interface ContextMenuProps { - children?: ReactNode, - } - - export interface ContextMenuTriggerProps { - children?: ReactNode, - } - - export interface MenuItemProps { - children?: ReactNode, - } -} \ No newline at end of file diff --git a/src/type-definitions/react-linkifier.d.ts b/src/type-definitions/react-linkifier.d.ts deleted file mode 100644 index afa7fca..0000000 --- a/src/type-definitions/react-linkifier.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -declare module "react-linkifier" { - import { Component, ReactNode } from "react"; - - export interface Props { - target?: string, - children: ReactNode, - } - - export default class Linkifier extends Component { } -} diff --git a/src/ui/About.tsx b/src/ui/About.tsx index 74d7618..b2a1b0a 100644 --- a/src/ui/About.tsx +++ b/src/ui/About.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import LiveSplitIcon from "../assets/icon.svg"; import { Markdown } from "../util/Markdown"; -import * as variables from "../css/variables.scss"; +import * as variables from "../css/variables.icss.scss"; import "../css/About.scss"; @@ -11,7 +11,7 @@ export interface Props { } interface Callbacks { - renderViewWithSidebar(renderedView: JSX.Element, sidebarContent: JSX.Element): JSX.Element, + renderViewWithSidebar(renderedView: React.JSX.Element, sidebarContent: React.JSX.Element): React.JSX.Element, openTimerView(): void, } diff --git a/src/ui/ColorPicker.tsx b/src/ui/ColorPicker.tsx index e3f24c3..9c698fb 100644 --- a/src/ui/ColorPicker.tsx +++ b/src/ui/ColorPicker.tsx @@ -1,9 +1,9 @@ -import * as React from "react"; +import React, { ChangeEvent, MouseEvent } from "react"; import { useEffect, useState } from "react"; import { colorToCss } from "../util/ColorUtil"; import { Color } from "../livesplit-core"; -import "../css/ColorPicker.scss"; +import * as classes from "../css/ColorPicker.module.scss"; const EyeDropper = (window as any).EyeDropper; const hasEyeDropper = !!EyeDropper; @@ -13,14 +13,11 @@ export default function ColorPicker({ color, setColor }: { color: Color, setColo return (
setIsShowing(true)} /> -
+
{isShowing && setIsShowing(false)} />}
@@ -29,67 +26,48 @@ export default function ColorPicker({ color, setColor }: { color: Color, setColo function ColorPickerDialog({ color, setColor, close }: { color: Color, setColor: (color: Color) => void, close: () => void }) { return ( -
-
close()}>
-
+ <> +
+


-
+ ); } function Hr() { - return ( -
- ); + return
; } function GradientSelector({ color, setColor }: { color: Color, setColor: (color: Color) => void }) { const [r, g, b, a] = color; const [h, s, v] = rgbToHsv(r, g, b); - const [r1, g1, b1] = hsvToRgb(h, 1.0, 1.0); + const [r1, g1, b1] = hsvToRgb(h, 1, 1); const [mouseDown, setMouseDown] = useState(false); - const handleMouseDown = (e: React.MouseEvent) => { + const handleMouseDown = (e: MouseEvent) => { e.preventDefault(); - (document.activeElement as any)?.blur(); + (document.activeElement as any)?.blur?.(); setMouseDown(true); updateColor(e); }; - const handleMouseUp = (e: React.MouseEvent) => { + const handleMouseUp = (e: MouseEvent) => { e.preventDefault(); setMouseDown(false); }; - const handleMouseMove = (e: React.MouseEvent) => { + const handleMouseMove = (e: MouseEvent) => { e.preventDefault(); if (!mouseDown) return; updateColor(e); }; - const updateColor = (e: React.MouseEvent) => { + const updateColor = (e: MouseEvent) => { const pos = e.currentTarget.getBoundingClientRect(); const x = Math.min(Math.max(e.clientX - pos.left, 0), 225); const y = Math.min(Math.max(e.clientY - pos.top, 0), 125); @@ -104,41 +82,19 @@ function GradientSelector({ color, setColor }: { color: Color, setColor: (color: onMouseDown={handleMouseDown} onMouseUp={handleMouseUp} onMouseMove={handleMouseMove} + className={classes.gradientSelector} style={{ background: `rgb(${255 * r1}, ${255 * g1}, ${255 * b1})`, - overflow: "hidden", - position: "relative", cursor: mouseDown ? "grabbing" : "grab", }} > -
-
-
+
+
+
-
+
); @@ -155,31 +111,23 @@ function ControlPanel({ color, setColor }: { color: Color, setColor: (color: Col const [h, s, v] = rgbToHsv(r, g, b); const [mode, setMode] = useState("Hex"); - const handleHueChange = (e: React.ChangeEvent) => { + const handleHueChange = (e: ChangeEvent) => { const newH = parseFloat(e.target.value); const [newR, newG, newB] = hsvToRgb(newH, s, v); setColor([newR, newG, newB, a]); }; - const handleAlphaChange = (e: React.ChangeEvent) => { + const handleAlphaChange = (e: ChangeEvent) => { const newA = parseFloat(e.target.value); setColor([r, g, b, newA]); }; return ( -
-
+
+
-
-
+
+
-
-
+
+
setMode(nextMode(mode))} > {mode === "Rgb" ? ( @@ -296,23 +219,14 @@ function ColorPreview({ color, setColor }: { color: Color, setColor: (color: Col /> )}
-
+
@@ -332,7 +246,7 @@ function Hsva({ color, setColor }: { color: Color, setColor: (color: Color) => v short="H" kind="Degree" value={h} - setValue={(newH: number) => { + setValue={(newH) => { const [newR, newG, newB] = hsvToRgb(newH, s, v); setColor([newR, newG, newB, a]); }} @@ -342,7 +256,7 @@ function Hsva({ color, setColor }: { color: Color, setColor: (color: Color) => v short="S" kind="Percent" value={s} - setValue={(newS: number) => { + setValue={(newS) => { const [newR, newG, newB] = hsvToRgb(h, newS, v); setColor([newR, newG, newB, a]); }} @@ -352,7 +266,7 @@ function Hsva({ color, setColor }: { color: Color, setColor: (color: Color) => v short="V" kind="Percent" value={v} - setValue={(newV: number) => { + setValue={(newV) => { const [newR, newG, newB] = hsvToRgb(h, s, newV); setColor([newR, newG, newB, a]); }} @@ -362,7 +276,7 @@ function Hsva({ color, setColor }: { color: Color, setColor: (color: Color) => v short="A" kind="Percent" value={a} - setValue={(newA: number) => { + setValue={(newA) => { setColor([r, g, b, newA]); }} /> @@ -380,7 +294,7 @@ function Rgba({ color, setColor }: { color: Color, setColor: (color: Color) => v short="R" kind="Byte" value={r} - setValue={(newR: number) => { + setValue={(newR) => { setColor([newR, g, b, a]); }} /> @@ -389,7 +303,7 @@ function Rgba({ color, setColor }: { color: Color, setColor: (color: Color) => v short="G" kind="Byte" value={g} - setValue={(newG: number) => { + setValue={(newG) => { setColor([r, newG, b, a]); }} /> @@ -398,7 +312,7 @@ function Rgba({ color, setColor }: { color: Color, setColor: (color: Color) => v short="B" kind="Byte" value={b} - setValue={(newB: number) => { + setValue={(newB) => { setColor([r, g, newB, a]); }} /> @@ -407,7 +321,7 @@ function Rgba({ color, setColor }: { color: Color, setColor: (color: Color) => v short="A" kind="Percent" value={a} - setValue={(newA: number) => { + setValue={(newA) => { setColor([r, g, b, newA]); }} /> @@ -420,11 +334,11 @@ function Hex({ color, setColor }: { color: Color, setColor: (color: Color) => vo return ( <> -
+
`#${Math.round(255 * r).toString(16).padStart(2, '0')}${Math.round(255 * g).toString(16).padStart(2, '0')}${Math.round(255 * b).toString(16).padStart(2, '0')}`.toUpperCase()} - parse={(value: string) => { + format={([r, g, b, _]) => `#${Math.round(255 * r).toString(16).padStart(2, '0')}${Math.round(255 * g).toString(16).padStart(2, '0')}${Math.round(255 * b).toString(16).padStart(2, '0')}`.toUpperCase()} + parse={(value) => { const parsed = parseHex(value); if (parsed) { return [...parsed, a]; @@ -440,7 +354,7 @@ function Hex({ color, setColor }: { color: Color, setColor: (color: Color) => vo short="A" kind="Percent" value={a} - setValue={(newA: number) => { + setValue={(newA) => { setColor([r, g, b, newA]); }} /> @@ -465,7 +379,7 @@ function FormattedInput({ value, format, parse, setValue }: { } }, [value]); - const handleInputChange = (e: React.ChangeEvent) => { + const handleInputChange = (e: ChangeEvent) => { const formatted = e.target.value; setFormatted(formatted); const newValue = parse(formatted); @@ -496,7 +410,7 @@ type Format = "Degree" | "Percent" | "Byte"; function ColorComponent({ title, short, kind, value, setValue }: { title: string, short: string, kind: Format, value: number, setValue: (value: number) => void }) { return ( -
+
kind === "Degree" ? `${value.toFixed(0)}°` : kind === "Percent" ? `${(100 * value).toFixed(0)}%` : `${(255 * value).toFixed(0)}`} @@ -517,40 +431,40 @@ function ColorComponent({ title, short, kind, value, setValue }: { title: string function PredefinedColors({ setColor }: { setColor: (color: Color) => void }) { const predefinedColors = [ [ - [4.0, 0.79, 0.96], - [340.0, 0.86, 0.91], - [291.0, 0.78, 0.69], - [262.0, 0.68, 0.71], - [231.0, 0.65, 0.71], - [207.0, 0.87, 0.95], - [199.0, 0.99, 0.95], + ["Red", 244, 67, 54], + ["Pink", 233, 30, 99], + ["Purple", 156, 39, 176], + ["Deep Purple", 103, 58, 183], + ["Indigo", 63, 81, 181], + ["Blue", 33, 150, 243], + ["Light Blue", 3, 169, 244], ], [ - [187.0, 1.0, 0.84], - [174.0, 1.0, 0.58], - [122.0, 0.56, 0.68], - [88.0, 0.61, 0.77], - [66.0, 0.75, 0.86], - [54.0, 0.76, 1.0], - [45.0, 0.98, 1.0], + ["Cyan", 0, 188, 212], + ["Teal", 0, 150, 136], + ["Green", 76, 175, 80], + ["Light Green", 139, 195, 74], + ["Lime", 205, 220, 57], + ["Yellow", 255, 235, 59], + ["Amber", 255, 193, 7], ], [ - [36.0, 1.0, 1.0], - [14.0, 0.86, 1.0], - [16.0, 0.40, 0.48], - [0.0, 0.0, 0.62], - [200.0, 0.31, 0.54], - [0.0, 0.0, 0.0], - [0.0, 0.0, 1.0], + ["Orange", 255, 152, 0], + ["Deep Orange", 255, 87, 34], + ["Brown", 121, 85, 72], + ["Grey", 158, 158, 158], + ["Blue Grey", 96, 125, 139], + ["Black", 0, 0, 0], + ["White", 255, 255, 255], ], - ] as unknown as [[[number, number, number]]]; + ] as unknown as [[[string, number, number, number]]]; return ( -
+
{predefinedColors.map((hsv, index) => ( -
+
{hsv.map((color, i) => ( - + ))}
))} @@ -558,36 +472,34 @@ function PredefinedColors({ setColor }: { setColor: (color: Color) => void }) { ); } -function PredefinedColor({ hsv, setColor }: { hsv: [number, number, number], setColor: (color: Color) => void }) { - const [h, s, v] = hsv; - const [r, g, b] = hsvToRgb(h, s, v); - +function PredefinedColor({ color: [title, r, g, b], setColor }: { color: [string, number, number, number], setColor: (color: Color) => void }) { return ( - - - this.addComponent(LiveSplit.TitleComponent)}> - Title - - Shows the name of the game and the category that is being run. Additionally, the game icon, the attempt count, and the total number of successfully finished runs can be shown. - - - this.addComponent(LiveSplit.GraphComponent)}> - Graph - - Visualizes how far the current run has been ahead or behind the chosen comparison throughout the whole run. All the individual deltas are shown as points on the graph. - - - this.addComponent(LiveSplit.SplitsComponent)}> - Splits - - The main component for visualizing all the split times. Each segment is shown in a tabular fashion showing the segment icon, segment name, the delta compared to the chosen comparison, and the split time. The list provides scrolling functionality, so not every segment needs to be shown all the time. - - - - this.addComponent(LiveSplit.DetailedTimerComponent)}> - Detailed Timer - - Shows two timers, one for the total time of the current run and one showing the time of just the current segment. Other information, like segment times of up to two comparisons, the segment icon, and the segment name, can also be shown. - - - this.addComponent(LiveSplit.TimerComponent)}> - Timer - - Shows the total time of the current run as a digital clock. The color of the time shown is based on a how well the current run is doing compared to the chosen comparison. - - - - this.addComponent(LiveSplit.CurrentComparisonComponent)}> - Current Comparison - - Shows the name of the comparison that the timer is currently comparing against. - - - this.addComponent(LiveSplit.CurrentPaceComponent)}> - Current Pace - - Shows a prediction for the current run's final time. The remainder of the run is predicted based on the chosen comparison for the component. For example, the "Best Segments" comparison can be chosen to show the best possible final time for the current run based on the Sum of Best Segments. - - - this.addComponent(LiveSplit.DeltaComponent)}> - Delta - - Shows how far ahead or behind the current run is compared to the chosen comparison. - - - this.addComponent(LiveSplit.PbChanceComponent)}> - PB Chance - - Shows how likely it is for the active run to beat the personal best. If there is no active run, it shows the general chance of beating the personal best. During a run, it actively changes based on how well the run is going. - - - this.addComponent(LiveSplit.PossibleTimeSaveComponent)}> - Possible Time Save - - Shows how much time you can save on the current segment compared to the chosen comparison, based on the best segment time of the segment. This component also allows showing the "Total Possible Time Save" for the remainder of the current run. - - - this.addComponent(LiveSplit.PreviousSegmentComponent)}> - Previous Segment - - Shows how much time was saved or lost during the previous segment based on the chosen comparison. Additionally, the potential time save for the previous segment can be displayed. This component switches to a "Live Segment" view that shows the active time loss whenever you are losing time on the current segment. - - - this.addComponent(LiveSplit.SegmentTimeComponent)}> - Segment Time - - Shows the time for the current segment for the chosen comparison. If no comparison is specified it uses the timer's current comparison. - - - this.addComponent(LiveSplit.SumOfBestComponent)}> - Sum of Best Segments - - Shows the fastest possible time to complete a run of the current category, based on information collected from all the previous runs. This often matches up with the sum of the best segment times of all the segments, but that may not always be the case, as skipped segments may introduce combined segments that may be faster than the actual sum of their best segment times. The name is therefore a bit misleading, but sticks around for historical reasons. - - - this.addComponent(LiveSplit.TextComponent)}> - Text - - Shows the text that you specify. This can either be a single centered text, or split up into a left and right text, which is suitable for a situation where you have a label and a value. There is also the option of showing a custom variable that you specify in the splits editor. - - - this.addComponent(LiveSplit.TotalPlaytimeComponent)}> - Total Playtime - - Shows the total amount of time that the current category has been played for. - - - { - this.props.allVariables.size > 0 && - } - { - this.props.allVariables.size > 0 && Array.from(this.props.allVariables).map((name) => { - return ( - this.addVariable(name)}> - {name} - - Creates a text component that shows the value of the custom variable "{name}". - - - ); - }) - } - - this.addComponent(LiveSplit.BlankSpaceComponent)}> - Blank Space - - An empty component that doesn't show anything other than a background. It mostly serves as padding between other components. - - - this.addComponent(LiveSplit.SeparatorComponent)}> - Separator - - A simple component that just renders a separator between components. - - - + this.addVariable(v)} + addComponent={(v) => this.addComponent(v)} + /> + {position && ( + setPosition(null)}> + addComponent(LiveSplit.TitleComponent)}> + Title + + Shows the name of the game and the category that is being run. Additionally, the game icon, the attempt count, and the total number of successfully finished runs can be shown. + + + addComponent(LiveSplit.GraphComponent)}> + Graph + + Visualizes how far the current run has been ahead or behind the chosen comparison throughout the whole run. All the individual deltas are shown as points on the graph. + + + addComponent(LiveSplit.SplitsComponent)}> + Splits + + The main component for visualizing all the split times. Each segment is shown in a tabular fashion showing the segment icon, segment name, the delta compared to the chosen comparison, and the split time. The list provides scrolling functionality, so not every segment needs to be shown all the time. + + + + addComponent(LiveSplit.DetailedTimerComponent)}> + Detailed Timer + + Shows two timers, one for the total time of the current run and one showing the time of just the current segment. Other information, like segment times of up to two comparisons, the segment icon, and the segment name, can also be shown. + + + addComponent(LiveSplit.TimerComponent)}> + Timer + + Shows the total time of the current run as a digital clock. The color of the time shown is based on a how well the current run is doing compared to the chosen comparison. + + + + addComponent(LiveSplit.CurrentComparisonComponent)}> + Current Comparison + + Shows the name of the comparison that the timer is currently comparing against. + + + addComponent(LiveSplit.CurrentPaceComponent)}> + Current Pace + + Shows a prediction for the current run's final time. The remainder of the run is predicted based on the chosen comparison for the component. For example, the "Best Segments" comparison can be chosen to show the best possible final time for the current run based on the Sum of Best Segments. + + + addComponent(LiveSplit.DeltaComponent)}> + Delta + + Shows how far ahead or behind the current run is compared to the chosen comparison. + + + addComponent(LiveSplit.PbChanceComponent)}> + PB Chance + + Shows how likely it is for the active run to beat the personal best. If there is no active run, it shows the general chance of beating the personal best. During a run, it actively changes based on how well the run is going. + + + addComponent(LiveSplit.PossibleTimeSaveComponent)}> + Possible Time Save + + Shows how much time you can save on the current segment compared to the chosen comparison, based on the best segment time of the segment. This component also allows showing the "Total Possible Time Save" for the remainder of the current run. + + + addComponent(LiveSplit.PreviousSegmentComponent)}> + Previous Segment + + Shows how much time was saved or lost during the previous segment based on the chosen comparison. Additionally, the potential time save for the previous segment can be displayed. This component switches to a "Live Segment" view that shows the active time loss whenever you are losing time on the current segment. + + + addComponent(LiveSplit.SegmentTimeComponent)}> + Segment Time + + Shows the time for the current segment for the chosen comparison. If no comparison is specified it uses the timer's current comparison. + + + addComponent(LiveSplit.SumOfBestComponent)}> + Sum of Best Segments + + Shows the fastest possible time to complete a run of the current category, based on information collected from all the previous runs. This often matches up with the sum of the best segment times of all the segments, but that may not always be the case, as skipped segments may introduce combined segments that may be faster than the actual sum of their best segment times. The name is therefore a bit misleading, but sticks around for historical reasons. + + + addComponent(LiveSplit.TextComponent)}> + Text + + Shows the text that you specify. This can either be a single centered text, or split up into a left and right text, which is suitable for a situation where you have a label and a value. There is also the option of showing a custom variable that you specify in the splits editor. + + + addComponent(LiveSplit.TotalPlaytimeComponent)}> + Total Playtime + + Shows the total amount of time that the current category has been played for. + + + { + allVariables.size > 0 && + } + { + allVariables.size > 0 && Array.from(allVariables).map((name) => { + return ( + addVariable(name)}> + {name} + + Creates a text component that shows the value of the custom variable "{name}". + + + ); + }) + } + + addComponent(LiveSplit.BlankSpaceComponent)}> + Blank Space + + An empty component that doesn't show anything other than a background. It mostly serves as padding between other components. + + + addComponent(LiveSplit.SeparatorComponent)}> + Separator + + A simple component that just renders a separator between components. + + + + )} + + ); +} diff --git a/src/ui/LayoutView.tsx b/src/ui/LayoutView.tsx index 0b56c93..3929b98 100644 --- a/src/ui/LayoutView.tsx +++ b/src/ui/LayoutView.tsx @@ -44,7 +44,7 @@ interface Callbacks { openSplitsView(): void, openMainSettings(): void, openTimerView(): void, - renderViewWithSidebar(renderedView: JSX.Element, sidebarContent: JSX.Element): JSX.Element, + renderViewWithSidebar(renderedView: React.JSX.Element, sidebarContent: React.JSX.Element): React.JSX.Element, saveLayout(): void, onServerConnectionOpened(serverConnection: LiveSplitServer): void, onServerConnectionClosed(): void, diff --git a/src/ui/LiveSplit.tsx b/src/ui/LiveSplit.tsx index 379ff68..0e72ac6 100644 --- a/src/ui/LiveSplit.tsx +++ b/src/ui/LiveSplit.tsx @@ -25,7 +25,7 @@ import { LSOCommandSink } from "./LSOCommandSink"; import DialogContainer from "./Dialog"; import { createHotkeys, HotkeyImplementation } from "../platform/Hotkeys"; -import * as variables from "../css/variables.scss"; +import * as variables from "../css/variables.icss.scss"; import "react-toastify/dist/ReactToastify.css"; import "../css/LiveSplit.scss"; @@ -426,7 +426,7 @@ export class LiveSplit extends React.Component { ; } - public renderViewWithSidebar(renderedView: JSX.Element, sidebarContent: JSX.Element) { + public renderViewWithSidebar(renderedView: React.JSX.Element, sidebarContent: React.JSX.Element) { return (
{ private renderView() { const gameIcon = this.getGameIcon(); - let gameIconContextTrigger: any = null; - const gameIconToggleMenu = (e: any) => { - if (gameIconContextTrigger) { - gameIconContextTrigger.handleContextClick(e); - } - }; - const tab = this.getTab(); const { category, categoryNames } = this.getCurrentCategoriesInfo(); @@ -145,58 +138,14 @@ export class RunEditor extends React.Component { return (
-
{ - gameIconToggleMenu(e); - }} - > - gameIconContextTrigger = c} - > - { - gameIcon !== undefined && - - } - -
- - this.changeGameIcon()}> - Set Icon - - Allows you to choose an image file to set as the game's icon. Certain file formats may not work everywhere. - - - { - this.props.generalSettings.speedrunComIntegration && <> - this.downloadBoxArt()}> - Download Box Art - - Attempts to download the box art of the game from speedrun.com, to set as the game's icon. - - - this.downloadIcon()}> - Download Icon - - Attempts to download the icon of the game from speedrun.com. - - - - } - { - gameIcon !== undefined && - this.removeGameIcon()}> - Remove Icon - - Removes the icon of the game. - - - } - + this.changeGameIcon()} + downloadBoxArt={() => this.downloadBoxArt()} + downloadIcon={() => this.downloadIcon()} + removeGameIcon={() => this.removeGameIcon()} + />
@@ -322,21 +271,7 @@ export class RunEditor extends React.Component { }); } - private renderSegmentListButtons(): JSX.Element { - let otherButtonContextTrigger: any = null; - const otherButtonToggleMenu = (e: any) => { - if (otherButtonContextTrigger) { - otherButtonContextTrigger.handleContextClick(e); - } - }; - - let comparisonsButtonContextTrigger: any = null; - const comparisonsButtonToggleMenu = (e: any) => { - if (comparisonsButtonContextTrigger) { - comparisonsButtonContextTrigger.handleContextClick(e); - } - }; - + private renderSegmentListButtons(): React.JSX.Element { return (
- - - - this.addComparison()}> - Add Comparison - - Adds a new custom comparison where you can store any times that you would like. - - - this.importComparison()}> - Import Comparison - - Imports the Personal Best of a splits file you provide as a comparison. - - - this.generateGoalComparison()}> - Generate Goal Comparison - - Generates a custom goal comparison based on a goal time that you can specify. The comparison's times are automatically balanced based on the segment history such that it roughly represents what the split times for the goal time would look like. Since it is populated by the segment history, the goal times are capped to a range between the sum of the best segments and the sum of the worst segments. The comparison is only populated for the selected timing method. The other timing method's comparison times are not modified by this, so you can generate it again with the other timing method to generate the comparison times for both timing methods. - - - this.copyComparison()}> - Copy Comparison - - Copies any existing comparison, including the Personal Best or even any other automatically provided comparison as a new custom comparison. You could for example use this to keep the Latest Run around as a comparison that exists for as long as you want it to. - - - - - this.clearHistory()}> - Clear Only History - - Splits store the entire history of all runs, including every segment time. This information is used by various components. You can clear the history with this. The personal best, the best segment times, and the comparisons will not be affected. - - - this.clearTimes()}> - Clear All Times - - This removes all the times from the splits, including all the history, such that the splits are completely empty, as if they were just created. - - - this.cleanSumOfBest()}> - Clean Sum of Best - - Allows you to interactively remove potential issues in the segment history that lead to an inaccurate Sum of Best. If you skip a split, whenever you will do the next split, the combined segment time might be faster than the sum of the individual best segments. This will point out all such occurrences and allow you to delete them individually if any of them seem wrong. - - - + this.addComparison()} + importComparison={() => this.importComparison()} + generateGoalComparison={() => this.generateGoalComparison()} + copyComparison={() => this.copyComparison()} + /> + this.clearHistory()} + clearTimes={() => this.clearTimes()} + cleanSumOfBest={() => this.cleanSumOfBest()} + />
); } - private renderAssociateRunButton(): JSX.Element { + private renderAssociateRunButton(): React.JSX.Element { if (this.props.generalSettings.speedrunComIntegration) { return (