diff --git a/package-lock.json b/package-lock.json index 8993265..746a67c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,16 @@ "name": "freechess", "version": "0.1.0", "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@fontsource/roboto": "^5.0.3", + "@iconify/react": "^4.1.0", + "@mui/material": "^5.13.4", "chess.js": "^1.0.0-beta.7", "jotai": "^2.6.4", "next": "13.5.6", "react": "18.2.0", + "react-chessboard": "^4.4.0", "react-dom": "18.2.0" }, "devDependencies": { @@ -37,18 +43,343 @@ "node": ">=0.10.0" } }, - "node_modules/@babel/runtime": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", - "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", - "dev": true, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz", + "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", + "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/styled": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", + "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -105,6 +436,45 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, + "node_modules/@fontsource/roboto": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.8.tgz", + "integrity": "sha512-XxPltXs5R31D6UZeLIV1td3wTXU3jzd3f2DLsXI8tytMGBkIsGcc9sIyiupRtA8y73HAhuSCeweOoBqf6DbWCA==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", @@ -138,6 +508,255 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@iconify/react": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@iconify/react/-/react-4.1.1.tgz", + "integrity": "sha512-jed14EjvKjee8mc0eoscGxlg7mSQRkwQG3iX3cPBCO7UlOjz0DtlvTqxqEcHUJGh+z1VJ31Yhu5B9PxfO0zbdg==", + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.36.tgz", + "integrity": "sha512-6A8fYiXgjqTO6pgj31Hc8wm1M3rFYCxDRh09dBVk0L0W4cb2lnurRJa3cAyic6hHY+we1S58OdGYRbKmOsDpGQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.13", + "@mui/utils": "^5.15.9", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.15.10", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.10.tgz", + "integrity": "sha512-qPv7B+LeMatYuzRjB3hlZUHqinHx/fX4YFBiaS19oC02A1e9JFuDKDvlyRQQ5oRSbJJt0QlaLTlr0IcauVcJRQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "5.15.10", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.10.tgz", + "integrity": "sha512-YJJGHjwDOucecjDEV5l9ISTCo+l9YeWrho623UajzoHRYxuKUmwrGVYOW4PKwGvCx9SU9oklZnbbi2Clc5XZHw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.36", + "@mui/core-downloads-tracker": "^5.15.10", + "@mui/system": "^5.15.9", + "@mui/types": "^7.2.13", + "@mui/utils": "^5.15.9", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@mui/private-theming": { + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.9.tgz", + "integrity": "sha512-/aMJlDOxOTAXyp4F2rIukW1O0anodAMCkv1DfBh/z9vaKHY3bd5fFf42wmP+0GRmwMinC5aWPpNfHXOED1fEtg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.9", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.9.tgz", + "integrity": "sha512-NRKtYkL5PZDH7dEmaLEIiipd3mxNnQSO+Yo8rFNBNptY8wzQnQ+VjayTq39qH7Sast5cwHKYFusUrQyD+SS4Og==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.9.tgz", + "integrity": "sha512-SxkaaZ8jsnIJ77bBXttfG//LUf6nTfOcaOuIgItqfHv60ZCQy/Hu7moaob35kBb+guxVJnoSZ+7vQJrA/E7pKg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.9", + "@mui/styled-engine": "^5.15.9", + "@mui/types": "^7.2.13", + "@mui/utils": "^5.15.9", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.13", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz", + "integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.9.tgz", + "integrity": "sha512-yDYfr61bCYUz1QtwvpqYy/3687Z8/nS4zv7lv/ih/6ZFGMl1iolEvxRmR84v2lOYxlds+kq1IVYbXxDKh8Z9sg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/@next/env": { "version": "13.5.6", "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.6.tgz", @@ -342,6 +961,30 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, "node_modules/@rushstack/eslint-patch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.1.tgz", @@ -372,19 +1015,22 @@ "version": "20.3.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.0.tgz", "integrity": "sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==", - "dev": true + "devOptional": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "devOptional": true + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/react": { "version": "18.2.11", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.11.tgz", "integrity": "sha512-+hsJr9hmwyDecSMQAmX7drgbDpyE+EgSF6t7+5QEBAn1tQK7kl1vWZ4iRf6SjQ8lk7dyEULxUmZOIpN0W5baZA==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -400,11 +1046,18 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "devOptional": true + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "node_modules/@types/semver": { "version": "7.5.0", @@ -824,6 +1477,20 @@ "deep-equal": "^2.0.5" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -916,15 +1583,14 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001499", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001499.tgz", - "integrity": "sha512-IhoQqRrW6WiecFcfZgoJS1YLEN1/HR1vHP5WNgjCARRW7KUNToHHTX3FrwCM+y4zkRa48D9rE90WFYc2IWhDWQ==", + "version": "1.0.30001588", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz", + "integrity": "sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==", "funding": [ { "type": "opencollective", @@ -966,6 +1632,14 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -990,6 +1664,26 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1005,10 +1699,9 @@ } }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "devOptional": true + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -1142,6 +1835,16 @@ "node": ">=8" } }, + "node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1154,6 +1857,15 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -1173,6 +1885,14 @@ "node": ">=10.13.0" } }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.21.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", @@ -1285,7 +2005,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -1801,8 +2520,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { "version": "1.3.0", @@ -1883,6 +2601,11 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1936,8 +2659,7 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -2141,7 +2863,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -2218,6 +2939,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/human-signals": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", @@ -2240,7 +2969,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2321,6 +3049,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -2365,7 +3098,6 @@ "version": "2.12.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -2704,6 +3436,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2769,6 +3506,11 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2985,7 +3727,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3196,7 +3937,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -3204,6 +3944,23 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3234,14 +3991,12 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -3331,7 +4086,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3378,6 +4132,66 @@ "node": ">=0.10.0" } }, + "node_modules/react-chessboard": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/react-chessboard/-/react-chessboard-4.4.0.tgz", + "integrity": "sha512-UJuFVju9pEcPJvH76HQyccm4uHyU755/Uf/3e1QfMHGWLFCBeEMSUkbzhlvxSL8Mutj/4wRxBVyJMyjhtnX0xg==", + "dependencies": { + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dnd-touch-backend": "^16.0.1" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dependencies": { + "dnd-core": "^16.0.1" + } + }, + "node_modules/react-dnd-touch-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-16.0.1.tgz", + "integrity": "sha512-NonoCABzzjyWGZuDxSG77dbgMZ2Wad7eQiCd/ECtsR2/NBLTjGksPUx9UPezZ1nQ/L7iD130Tz3RUshL/ClKLA==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "dnd-core": "^16.0.1" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -3393,14 +4207,35 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.0", @@ -3423,7 +4258,6 @@ "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, "dependencies": { "is-core-module": "^2.11.0", "path-parse": "^1.0.7", @@ -3440,7 +4274,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -3693,6 +4526,14 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -3852,6 +4693,11 @@ } } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3868,7 +4714,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3919,6 +4764,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4143,6 +4996,14 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index f5a66ca..32aee56 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,16 @@ "deploy": "firebase deploy --project=freechess --only hosting" }, "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@fontsource/roboto": "^5.0.3", + "@iconify/react": "^4.1.0", + "@mui/material": "^5.13.4", "chess.js": "^1.0.0-beta.7", "jotai": "^2.6.4", "next": "13.5.6", "react": "18.2.0", + "react-chessboard": "^4.4.0", "react-dom": "18.2.0" }, "devDependencies": { diff --git a/src/components/NavLink.tsx b/src/components/NavLink.tsx new file mode 100644 index 0000000..b9ed5af --- /dev/null +++ b/src/components/NavLink.tsx @@ -0,0 +1,23 @@ +import { Link as MuiLink } from "@mui/material"; +import NextLink from "next/link"; +import { ReactNode } from "react"; + +export default function NavLink({ + href, + children, +}: { + href: string; + children: ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/src/hooks/useChess.ts b/src/hooks/useChess.ts new file mode 100644 index 0000000..3af609d --- /dev/null +++ b/src/hooks/useChess.ts @@ -0,0 +1,36 @@ +import { Chess } from "chess.js"; +import { PrimitiveAtom, useAtom } from "jotai"; + +export const useChessActions = (chessAtom: PrimitiveAtom) => { + const [game, setGame] = useAtom(chessAtom); + + const setPgn = (pgn: string) => { + const newGame = new Chess(); + newGame.loadPgn(pgn); + setGame(newGame); + }; + + const reset = () => { + setGame(new Chess()); + }; + + const copyGame = () => { + const newGame = new Chess(); + newGame.loadPgn(game.pgn()); + return newGame; + }; + + const move = (move: { from: string; to: string; promotion?: string }) => { + const newGame = copyGame(); + newGame.move(move); + setGame(newGame); + }; + + const undo = () => { + const newGame = copyGame(); + newGame.undo(); + setGame(newGame); + }; + + return { setPgn, reset, move, undo }; +}; diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts new file mode 100644 index 0000000..86e28f3 --- /dev/null +++ b/src/hooks/useLocalStorage.ts @@ -0,0 +1,29 @@ +import { Dispatch, SetStateAction, useEffect, useState } from "react"; + +type SetValue = Dispatch>; + +export function useLocalStorage( + key: string, + initialValue: T +): [T, SetValue] { + const [storedValue, setStoredValue] = useState(initialValue); + + useEffect(() => { + const item = window.localStorage.getItem(key); + if (item) { + setStoredValue(parseJSON(item)); + } + }, [key]); + + const setValue: SetValue = (value) => { + const newValue = value instanceof Function ? value(storedValue) : value; + window.localStorage.setItem(key, JSON.stringify(newValue)); + setStoredValue(newValue); + }; + + return [storedValue, setValue]; +} + +function parseJSON(value: string): T { + return value === "undefined" ? undefined : JSON.parse(value); +} diff --git a/src/lib/board.ts b/src/lib/board.ts deleted file mode 100644 index 31228c0..0000000 --- a/src/lib/board.ts +++ /dev/null @@ -1,268 +0,0 @@ -const boardFlipped = false; -const currentMoveIndex = 0; -const reportResults: any = undefined; - -export async function drawBoard( - ctx: CanvasRenderingContext2D, - fen = startingPositionFen -) { - // Draw surface of board - let colours = ["#f6dfc0", "#b88767"]; - - for (let y = 0; y < 8; y++) { - for (let x = 0; x < 8; x++) { - ctx.fillStyle = colours[(x + y) % 2]; - - ctx.fillRect(x * 90, y * 90, 90, 90); - } - } - - // Draw coordinates - ctx.font = "20px Arial"; - - let files = "abcdefgh".split(""); - for (let x = 0; x < 8; x++) { - ctx.fillStyle = colours[x % 2]; - ctx.fillText(boardFlipped ? files[7 - x] : files[x], x * 90 + 5, 715); - } - for (let y = 0; y < 8; y++) { - ctx.fillStyle = colours[(y + 1) % 2]; - ctx.fillText( - boardFlipped ? (y + 1).toString() : (8 - y).toString(), - 5, - y * 90 + 22 - ); - } - - // Draw last move highlight and top move arrows - let lastMove = reportResults?.positions[currentMoveIndex]; - - let lastMoveCoordinates = { - from: { x: 0, y: 0 }, - to: { x: 0, y: 0 }, - }; - - if (currentMoveIndex > 0 && lastMove) { - let lastMoveUCI = lastMove.move?.uci; - if (!lastMoveUCI) return; - - lastMoveCoordinates.from = getBoardCoordinates(lastMoveUCI.slice(0, 2)); - lastMoveCoordinates.to = getBoardCoordinates(lastMoveUCI.slice(2, 4)); - - ctx.globalAlpha = 0.7; - ctx.fillStyle = - classificationColours[ - reportResults?.positions[currentMoveIndex].classification ?? "book" - ]; - ctx.fillRect( - lastMoveCoordinates.from.x * 90, - lastMoveCoordinates.from.y * 90, - 90, - 90 - ); - ctx.fillRect( - lastMoveCoordinates.to.x * 90, - lastMoveCoordinates.to.y * 90, - 90, - 90 - ); - ctx.globalAlpha = 1; - } - - // Draw pieces - let fenBoard = fen.split(" ")[0]; - let x = boardFlipped ? 7 : 0, - y = x; - - for (let character of fenBoard) { - if (character == "/") { - x = boardFlipped ? 7 : 0; - y += boardFlipped ? -1 : 1; - } else if (/\d/g.test(character)) { - x += parseInt(character) * (boardFlipped ? -1 : 1); - } else { - const pieceSrc = pieceImagesSrc[character]; - if (!pieceSrc) throw new Error(`No image source for piece ${character}`); - const pieceImage = await loadImage(pieceSrc); - - ctx.drawImage(pieceImage, x * 90, y * 90, 90, 90); - x += boardFlipped ? -1 : 1; - } - } - - // Draw last move classification - if (currentMoveIndex > 0 && reportResults) { - let classification = - reportResults?.positions[currentMoveIndex]?.classification; - - if (!classification) return; - - const iconSrc = classificationIconsSrc[classification]; - if (!iconSrc) - throw new Error(`No image source for classification ${classification}`); - const classificationIcon = await loadImage(iconSrc); - - ctx.drawImage( - classificationIcon, - lastMoveCoordinates.to.x * 90 + 68, - lastMoveCoordinates.to.y * 90 - 10, - 32, - 32 - ); - } - - // Draw engine suggestion arrows - if (true) { - let arrowAttributes = [ - { - width: 20, - opacity: 0.8, - }, - { - width: 12, - opacity: 0.55, - }, - ]; - - let topLineIndex = -1; - for (let topLine of lastMove?.topLines ?? []) { - topLineIndex++; - - let from = getBoardCoordinates(topLine.moveUCI.slice(0, 2)); - let to = getBoardCoordinates(topLine.moveUCI.slice(2, 4)); - - let arrow = drawArrow( - from.x * 90 + 45, - from.y * 90 + 45, - to.x * 90 + 45, - to.y * 90 + 45, - arrowAttributes[topLineIndex].width, - ctx - ); - if (!arrow) continue; - - ctx.globalAlpha = arrowAttributes[topLineIndex].opacity; - ctx.drawImage(arrow, 0, 0); - ctx.globalAlpha = 1; - } - } -} - -function getBoardCoordinates(square: string): { x: number; y: number } { - if (boardFlipped) { - return { - x: 7 - "abcdefgh".split("").indexOf(square.slice(0, 1)), - y: parseInt(square.slice(1)) - 1, - }; - } else { - return { - x: "abcdefgh".split("").indexOf(square.slice(0, 1)), - y: 8 - parseInt(square.slice(1)), - }; - } -} - -function drawArrow( - fromX: number, - fromY: number, - toX: number, - toY: number, - width: number, - arrowCtx: CanvasRenderingContext2D -) { - if (!arrowCtx) return; - - arrowCtx.canvas.width = 720; - arrowCtx.canvas.height = 720; - - let headlen = 15; - let angle = Math.atan2(toY - fromY, toX - fromX); - toX -= Math.cos(angle) * (width * 1.15); - toY -= Math.sin(angle) * (width * 1.15); - - arrowCtx.beginPath(); - arrowCtx.moveTo(fromX, fromY); - arrowCtx.lineTo(toX, toY); - arrowCtx.strokeStyle = classificationColours.best; - arrowCtx.lineWidth = width; - arrowCtx.stroke(); - - arrowCtx.beginPath(); - arrowCtx.moveTo(toX, toY); - arrowCtx.lineTo( - toX - headlen * Math.cos(angle - Math.PI / 7), - toY - headlen * Math.sin(angle - Math.PI / 7) - ); - - arrowCtx.lineTo( - toX - headlen * Math.cos(angle + Math.PI / 7), - toY - headlen * Math.sin(angle + Math.PI / 7) - ); - - arrowCtx.lineTo(toX, toY); - arrowCtx.lineTo( - toX - headlen * Math.cos(angle - Math.PI / 7), - toY - headlen * Math.sin(angle - Math.PI / 7) - ); - - arrowCtx.strokeStyle = classificationColours.best; - arrowCtx.lineWidth = width; - arrowCtx.stroke(); - arrowCtx.fillStyle = classificationColours.best; - arrowCtx.fill(); - - return arrowCtx.canvas; -} - -const startingPositionFen = - "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; - -const classificationColours: { [key: string]: string } = { - brilliant: "#1baaa6", - great: "#5b8baf", - best: "#98bc49", - excellent: "#98bc49", - good: "#97af8b", - inaccuracy: "#f4bf44", - mistake: "#e28c28", - blunder: "#c93230", - forced: "#97af8b", - book: "#a88764", -}; - -const pieceImagesSrc: Record = { - P: "white_pawn.svg", - N: "white_knight.svg", - B: "white_bishop.svg", - R: "white_rook.svg", - Q: "white_queen.svg", - K: "white_king.svg", - p: "black_pawn.svg", - n: "black_knight.svg", - b: "black_bishop.svg", - r: "black_rook.svg", - q: "black_queen.svg", - k: "black_king.svg", -}; - -const classificationIconsSrc: Record = { - brilliant: "brilliant.png", - great: "great.png", - best: "best.png", - excellent: "excellent.png", - good: "good.png", - inaccuracy: "inaccuracy.png", - mistake: "mistake.png", - blunder: "blunder.png", - forced: "forced.png", - book: "book.png", -}; - -async function loadImage(filename: string): Promise { - return new Promise((res) => { - const image = new Image(); - image.src = filename; - - image.addEventListener("load", () => res(image)); - }); -} diff --git a/src/lib/chess.ts b/src/lib/chess.ts index 953f184..e3898b2 100644 --- a/src/lib/chess.ts +++ b/src/lib/chess.ts @@ -1,64 +1,7 @@ import { Chess } from "chess.js"; -export const initPgn = new Chess().pgn(); - -export const getGameFens = (pgn: string): string[] => { +export const pgnToFens = (pgn: string): string[] => { const game = new Chess(); game.loadPgn(pgn); return game.history({ verbose: true }).map((move) => move.before); }; - -export const getLastFen = (pgn: string): string => { - const game = new Chess(); - game.loadPgn(pgn); - return game.fen(); -}; - -export const undoLastMove = (pgn: string): string => { - if (pgn === initPgn) return pgn; - const game = new Chess(); - game.loadPgn(pgn); - game.undo(); - return game.pgn(); -}; - -export const addMove = ( - pgn: string, - move: { - from: string; - to: string; - promotion?: string; - } -): string => { - const game = new Chess(); - game.loadPgn(pgn); - game.move(move); - return game.pgn(); -}; - -export const addNextMove = (boardPgn: string, gamePgn: string): string => { - const board = new Chess(); - board.loadPgn(boardPgn); - - const game = new Chess(); - game.loadPgn(gamePgn); - - const nextMoveIndex = board.history().length; - const nextMove = game.history({ verbose: true })[nextMoveIndex]; - - if (nextMove) { - board.move({ - from: nextMove.from, - to: nextMove.to, - promotion: nextMove.promotion, - }); - } - - return board.pgn(); -}; - -export const getNextMoveIndex = (pgn: string): number => { - const game = new Chess(); - game.loadPgn(pgn); - return game.history().length; -}; diff --git a/src/lib/engine/stockfish.ts b/src/lib/engine/stockfish.ts index 6b9fbb4..d631c0e 100644 --- a/src/lib/engine/stockfish.ts +++ b/src/lib/engine/stockfish.ts @@ -2,7 +2,7 @@ import { GameEval, LineEval, MoveEval } from "@/types/eval"; export class Stockfish { private worker: Worker; - private ready: boolean = false; + private ready = false; constructor() { this.worker = new Worker( @@ -14,7 +14,7 @@ export class Stockfish { console.log("Stockfish created"); } - public isWasmSupported(): boolean { + public isWasmSupported() { return ( typeof WebAssembly === "object" && WebAssembly.validate( diff --git a/src/lib/evalBar.ts b/src/lib/evalBar.ts deleted file mode 100644 index 925c577..0000000 --- a/src/lib/evalBar.ts +++ /dev/null @@ -1,55 +0,0 @@ -export async function drawEvaluationBar( - evaluationBarCtx: CanvasRenderingContext2D, - evaluation = initialEvaluation -) { - evaluationBarCtx.clearRect(0, 0, 30, 720); - evaluationBarCtx.font = "16px Arial"; - evaluationBarCtx.fillStyle = "#1e1e1e"; - - if (evaluation.type == "cp") { - let height = Math.max(Math.min(360 - evaluation.value / 3, 680), 40); - evaluationBarCtx.fillRect(0, 0, 30, height); - - let evaluationText = Math.abs(evaluation.value / 100).toFixed(1); - let evaluationTextWidth = - evaluationBarCtx.measureText(evaluationText).width; - - let evaluationTextY = evaluation.value >= 0 ? 710 : 20; - evaluationBarCtx.fillStyle = evaluation.value >= 0 ? "#1e1e1e" : "#ffffff"; - evaluationBarCtx.fillText( - evaluationText, - 15 - evaluationTextWidth / 2, - evaluationTextY, - 30 - ); - } else { - let evaluationText = "M" + Math.abs(evaluation.value).toString(); - let evaluationTextWidth = - evaluationBarCtx.measureText(evaluationText).width; - - if (evaluation.value > 0) { - evaluationBarCtx.fillStyle = "#1e1e1e"; - evaluationBarCtx.fillText( - evaluationText, - 15 - evaluationTextWidth / 2, - 710, - 30 - ); - } else if (evaluation.value < 0) { - evaluationBarCtx.fillRect(0, 0, 30, 720); - - evaluationBarCtx.fillStyle = "#ffffff"; - evaluationBarCtx.fillText( - evaluationText, - 15 - evaluationTextWidth / 2, - 20, - 30 - ); - } else if (evaluation.value == 0) { - evaluationBarCtx.fillStyle = "#676767"; - evaluationBarCtx.fillRect(0, 0, 30, 720); - } - } -} - -const initialEvaluation = { type: "cp", value: 0 }; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index c7611eb..6be13a1 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,7 +1,12 @@ +import "@fontsource/roboto/300.css"; +import "@fontsource/roboto/400.css"; +import "@fontsource/roboto/500.css"; +import "@fontsource/roboto/700.css"; import { AppProps } from "next/app"; import Head from "next/head"; -import "../../styles/global.css"; -import "../../styles/index.css"; +// import "../../styles/global.css"; +// import "../../styles/index.css"; +import Layout from "@/sections/layout"; export default function MyApp({ Component, pageProps }: AppProps) { return ( @@ -9,7 +14,9 @@ export default function MyApp({ Component, pageProps }: AppProps) { Free Chess - + + + ); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index d4068ba..fd02f93 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,24 +1,53 @@ -import TopBar from "@/sections/index/topBar"; -import Board from "@/sections/index/board"; -import ReviewPanelBody from "@/sections/index/reviewPanelBody"; -import ReviewPanelToolBar from "@/sections/index/reviewPanelToolbar"; +import Board from "@/sections/gameReport/board"; +import ReviewPanelBody from "@/sections/gameReport/reviewPanelBody"; +import ReviewPanelHeader from "@/sections/gameReport/reviewPanelHeader"; +import ReviewPanelToolBar from "@/sections/gameReport/reviewPanelToolbar"; +import { Grid } from "@mui/material"; -export default function HomePage() { +export default function GameReport() { return ( - <> - + + -
-
- + + + -
- + - -
-
-
- + +
+ + ); } diff --git a/src/sections/gameReport/board.tsx b/src/sections/gameReport/board.tsx new file mode 100644 index 0000000..6dc5e8a --- /dev/null +++ b/src/sections/gameReport/board.tsx @@ -0,0 +1,28 @@ +import { Grid, Typography } from "@mui/material"; +import { Chessboard } from "react-chessboard"; +import { useAtomValue } from "jotai"; +import { boardAtom } from "./states"; + +export default function Board() { + const board = useAtomValue(boardAtom); + + return ( + + + White Player (?) + + + + Black Player (?) + + + ); +} diff --git a/src/sections/gameReport/reviewPanelBody.tsx b/src/sections/gameReport/reviewPanelBody.tsx new file mode 100644 index 0000000..7ee78c4 --- /dev/null +++ b/src/sections/gameReport/reviewPanelBody.tsx @@ -0,0 +1,66 @@ +import { Icon } from "@iconify/react"; +import { + Divider, + Grid, + List, + ListItem, + ListItemText, + Typography, +} from "@mui/material"; +import { useAtomValue } from "jotai"; +import { boardAtom, gameEvalAtom } from "./states"; + +export default function ReviewPanelBody() { + const board = useAtomValue(boardAtom); + const gameEval = useAtomValue(gameEvalAtom); + if (!gameEval) return null; + + const evalIndex = board.history().length; + const moveEval = gameEval.moves[evalIndex]; + + return ( + <> + + + + + + Bilan de la partie + + + + + {moveEval ? `${moveEval.bestMove} is the best move` : "Game is over"} + + + + + {moveEval?.lines.map((line) => ( + + + {line.pv.slice(0, 7).join(", ")} + + ))} + + + + ); +} diff --git a/src/sections/gameReport/reviewPanelHeader.tsx b/src/sections/gameReport/reviewPanelHeader.tsx new file mode 100644 index 0000000..8b6acc5 --- /dev/null +++ b/src/sections/gameReport/reviewPanelHeader.tsx @@ -0,0 +1,58 @@ +import { useEffect, useState } from "react"; +import SelectGameOrigin from "./selectGame/selectGameOrigin"; +import { Stockfish } from "@/lib/engine/stockfish"; +import { Icon } from "@iconify/react"; +import { Button, Typography } from "@mui/material"; +import { useAtomValue, useSetAtom } from "jotai"; +import { boardAtom, gameAtom, gameEvalAtom } from "./states"; +import { useChessActions } from "@/hooks/useChess"; +import { gameInputPgnAtom } from "./selectGame/gameInput.states"; +import { pgnToFens } from "@/lib/chess"; + +export default function ReviewPanelHeader() { + const [engine, setEngine] = useState(null); + const setEval = useSetAtom(gameEvalAtom); + const boardActions = useChessActions(boardAtom); + const gameActions = useChessActions(gameAtom); + const pgnInput = useAtomValue(gameInputPgnAtom); + + useEffect(() => { + const engine = new Stockfish(); + engine.init(); + setEngine(engine); + + return () => { + engine.shutdown(); + }; + }, []); + + const handleAnalyse = async () => { + boardActions.reset(); + gameActions.setPgn(pgnInput); + const gameFens = pgnToFens(pgnInput); + if (engine?.isReady() && gameFens.length) { + const newGameEval = await engine.evaluateGame(gameFens); + setEval(newGameEval); + } + }; + + return ( + <> + + + Game Report + + + + + + + ); +} diff --git a/src/sections/gameReport/reviewPanelToolbar.tsx b/src/sections/gameReport/reviewPanelToolbar.tsx new file mode 100644 index 0000000..5ed475a --- /dev/null +++ b/src/sections/gameReport/reviewPanelToolbar.tsx @@ -0,0 +1,51 @@ +import { Divider, Grid, IconButton } from "@mui/material"; +import { Icon } from "@iconify/react"; +import { useAtomValue } from "jotai"; +import { boardAtom, gameAtom } from "./states"; +import { useChessActions } from "@/hooks/useChess"; + +export default function ReviewPanelToolBar() { + const game = useAtomValue(gameAtom); + const board = useAtomValue(boardAtom); + const boardActions = useChessActions(boardAtom); + + const addNextMoveToGame = () => { + const nextMoveIndex = board.history().length; + const nextMove = game.history({ verbose: true })[nextMoveIndex]; + + if (nextMove) { + boardActions.move({ + from: nextMove.from, + to: nextMove.to, + promotion: nextMove.promotion, + }); + } + }; + + return ( + <> + + + + + + + boardActions.reset()}> + + + boardActions.undo()}> + + + addNextMoveToGame()}> + + + + + + + + + + + ); +} diff --git a/src/sections/gameReport/selectGame/gameInput.states.ts b/src/sections/gameReport/selectGame/gameInput.states.ts new file mode 100644 index 0000000..a7e1ca0 --- /dev/null +++ b/src/sections/gameReport/selectGame/gameInput.states.ts @@ -0,0 +1,3 @@ +import { atom } from "jotai"; + +export const gameInputPgnAtom = atom(""); diff --git a/src/sections/gameReport/selectGame/inputGame.tsx b/src/sections/gameReport/selectGame/inputGame.tsx new file mode 100644 index 0000000..71fb7be --- /dev/null +++ b/src/sections/gameReport/selectGame/inputGame.tsx @@ -0,0 +1,19 @@ +import { TextField } from "@mui/material"; +import { useAtom } from "jotai"; +import { gameInputPgnAtom } from "./gameInput.states"; + +export default function InputGame() { + const [pgn, setPgn] = useAtom(gameInputPgnAtom); + + return ( + { + setPgn(e.target.value); + }} + /> + ); +} diff --git a/src/sections/gameReport/selectGame/selectGameOrigin.tsx b/src/sections/gameReport/selectGame/selectGameOrigin.tsx new file mode 100644 index 0000000..a2f226d --- /dev/null +++ b/src/sections/gameReport/selectGame/selectGameOrigin.tsx @@ -0,0 +1,46 @@ +import { GameOrigin } from "@/types/enums"; +import { FormControl, Grid, InputLabel, MenuItem, Select } from "@mui/material"; +import InputGame from "./inputGame"; +import { useState } from "react"; + +export default function SelectGameOrigin() { + const [gameOrigin, setGameOrigin] = useState(GameOrigin.Pgn); + + return ( + + + Game Origin + + + + + + ); +} + +const gameOriginLabel: Record = { + [GameOrigin.Pgn]: "PGN", + [GameOrigin.ChessCom]: "Chess.com", + [GameOrigin.Lichess]: "Lichess", + [GameOrigin.Json]: "JSON", +}; diff --git a/src/sections/index/index.state.ts b/src/sections/gameReport/states.ts similarity index 52% rename from src/sections/index/index.state.ts rename to src/sections/gameReport/states.ts index 3c3311f..59bee2d 100644 --- a/src/sections/index/index.state.ts +++ b/src/sections/gameReport/states.ts @@ -1,7 +1,7 @@ -import { initPgn } from "@/lib/chess"; import { GameEval } from "@/types/eval"; +import { Chess } from "chess.js"; import { atom } from "jotai"; export const gameEvalAtom = atom(undefined); -export const gamePgnAtom = atom(initPgn); -export const boardPgnAtom = atom(initPgn); +export const gameAtom = atom(new Chess()); +export const boardAtom = atom(new Chess()); diff --git a/src/sections/index/board.tsx b/src/sections/index/board.tsx deleted file mode 100644 index c06f014..0000000 --- a/src/sections/index/board.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { drawBoard } from "@/lib/board"; -import { drawEvaluationBar } from "@/lib/evalBar"; -import { useAtomValue } from "jotai"; -import { useEffect, useRef } from "react"; -import { boardPgnAtom } from "./index.state"; -import { getLastFen } from "@/lib/chess"; - -export default function Board() { - const boardRef = useRef(null); - const evalBarRef = useRef(null); - const boardPgn = useAtomValue(boardPgnAtom); - const boardFen = getLastFen(boardPgn); - - useEffect(() => { - const ctx = boardRef.current?.getContext("2d"); - if (!ctx) return; - - drawBoard(ctx, boardFen); - - const evalCtx = evalBarRef.current?.getContext("2d"); - if (!evalCtx) return; - drawEvaluationBar(evalCtx); - }, [boardFen]); - - return ( -
- - -
-
- Black Player (?) -
- - - -
- White Player (?) -
-
-
- ); -} diff --git a/src/sections/index/reviewPanelBody.tsx b/src/sections/index/reviewPanelBody.tsx deleted file mode 100644 index fe34a49..0000000 --- a/src/sections/index/reviewPanelBody.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useEffect, useState } from "react"; -import ReviewResult from "./reviewResult"; -import SelectDepth from "./selectDepth"; -import SelectGameOrigin from "./selectGame/selectGameOrigin"; -import { Stockfish } from "@/lib/engine/stockfish"; -import { useAtomValue, useSetAtom } from "jotai"; -import { boardPgnAtom, gameEvalAtom, gamePgnAtom } from "./index.state"; -import { getGameFens, initPgn } from "@/lib/chess"; - -export default function ReviewPanelBody() { - const [engine, setEngine] = useState(null); - const setGameEval = useSetAtom(gameEvalAtom); - const setBoardPgn = useSetAtom(boardPgnAtom); - const gamePgn = useAtomValue(gamePgnAtom); - - useEffect(() => { - const engine = new Stockfish(); - engine.init(); - setEngine(engine); - - return () => { - engine.shutdown(); - }; - }, []); - - const handleAnalyse = async () => { - setBoardPgn(initPgn); - const gameFens = getGameFens(gamePgn); - if (engine?.isReady() && gameFens.length) { - const newGameEval = await engine.evaluateGame(gameFens); - setGameEval(newGameEval); - } - }; - - return ( -
-

- 📑 Game Report -

- - - - - - - - {false && } - - - - - - -
- ); -} diff --git a/src/sections/index/reviewPanelToolbar.tsx b/src/sections/index/reviewPanelToolbar.tsx deleted file mode 100644 index fd0b6f3..0000000 --- a/src/sections/index/reviewPanelToolbar.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { useAtom, useAtomValue } from "jotai"; -import { boardPgnAtom, gamePgnAtom } from "./index.state"; -import { addNextMove, initPgn, undoLastMove } from "@/lib/chess"; - -export default function ReviewPanelToolBar() { - const [boardPgn, setBoardPgn] = useAtom(boardPgnAtom); - const gamePgn = useAtomValue(gamePgnAtom); - - return ( -
-
- Flip Board - Back to start setBoardPgn(initPgn)} - /> - Back { - setBoardPgn(undoLastMove(boardPgn)); - }} - /> - Next { - const nextBoardPgn = addNextMove(boardPgn, gamePgn); - setBoardPgn(nextBoardPgn); - }} - /> - Go to end - Save analysis -
- -
- - Suggestion Arrows -
-
- ); -} diff --git a/src/sections/index/reviewResult.tsx b/src/sections/index/reviewResult.tsx deleted file mode 100644 index ca3b5e8..0000000 --- a/src/sections/index/reviewResult.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useAtomValue } from "jotai"; -import { boardPgnAtom, gameEvalAtom } from "./index.state"; -import { getNextMoveIndex } from "@/lib/chess"; - -export default function ReviewResult() { - const boardPgn = useAtomValue(boardPgnAtom); - const gameEval = useAtomValue(gameEvalAtom); - if (!gameEval) return null; - - const evalIndex = getNextMoveIndex(boardPgn); - const moveEval = gameEval.moves[evalIndex]; - - return ( -
-

- Accuracies -

-
- {gameEval.whiteAccuracy.toFixed(1)}% - {gameEval.blackAccuracy.toFixed(1)}% -
- -
- - -
- - - {moveEval ? `${moveEval.bestMove} is best` : "Game is over"} - - -
-

- Engine -

- {moveEval?.lines.map((line) => ( -
- - {line.cp !== undefined - ? line.cp / 100 - : `Mate in ${Math.abs(line.mate ?? 0)}`} - - {line.pv.slice(0, 7).join(", ")} -
- ))} -
- - -
- ); -} diff --git a/src/sections/index/selectDepth.tsx b/src/sections/index/selectDepth.tsx deleted file mode 100644 index b87e445..0000000 --- a/src/sections/index/selectDepth.tsx +++ /dev/null @@ -1,22 +0,0 @@ -export default function SelectDepth() { - return ( - <> - ⚙️ Search depth -
- - - 16 🐇 - -
-
- Lower depths recommended for slower devices. -
- - ); -} diff --git a/src/sections/index/selectGame/inputGame.tsx b/src/sections/index/selectGame/inputGame.tsx deleted file mode 100644 index 3fbc679..0000000 --- a/src/sections/index/selectGame/inputGame.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { GameOrigin } from "@/types/enums"; -import { useAtom } from "jotai"; -import { gamePgnAtom } from "../index.state"; - -interface Props { - gameOrigin: GameOrigin; - placeholder?: string; -} - -export default function InputGame({ placeholder }: Props) { - const [gamePgn, setGamePgn] = useAtom(gamePgnAtom); - - return ( -