diff --git a/package-lock.json b/package-lock.json index ddda2d4..781ddc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,8 @@ "immer": "^10.0.2" }, "devDependencies": { - "lerna": "^7.0.2" + "lerna": "^7.0.2", + "typescript": "^5.1.6" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -3727,14 +3728,14 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -3784,9 +3785,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4138,43 +4139,10 @@ "node": ">=8" } }, - "node_modules/@mantine/core": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/core/-/core-6.0.14.tgz", - "integrity": "sha512-O916itwsB5XtEStVZRqp3SlB4aLM0sSgOFYYCVnLJ3E9O9E8h1xhaNEml1FJbMtrlNaXYUd6sy/OSRqNl9DyKA==", - "dependencies": { - "@floating-ui/react": "^0.19.1", - "@mantine/styles": "6.0.14", - "@mantine/utils": "6.0.14", - "@radix-ui/react-scroll-area": "1.0.2", - "react-remove-scroll": "^2.5.5", - "react-textarea-autosize": "8.3.4" - }, - "peerDependencies": { - "@mantine/hooks": "6.0.14", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@mantine/dropzone": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-6.0.14.tgz", - "integrity": "sha512-rurn7wfqMx4vuU3glS2UpwbKId+TB7H1mY4s5Z2YExMb5ffty58G+io10ACKIhQ7iTgcvxpK9fyFVLsnRHT64A==", - "dependencies": { - "@mantine/utils": "6.0.14", - "react-dropzone": "14.2.3" - }, - "peerDependencies": { - "@mantine/core": "6.0.14", - "@mantine/hooks": "6.0.14", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, "node_modules/@mantine/form": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/form/-/form-6.0.14.tgz", - "integrity": "sha512-2QlDN3PBMxHUxtoBy0ycc3InpATGje5sJXmw/Co9qiVtKUHe5pxcVl341CnA+MCI91uC2Ycucf20n/8GTLezrw==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/form/-/form-6.0.16.tgz", + "integrity": "sha512-4TwxJKGQQRx7rj5yb9WgS0z/Ud8ckg0mMCiD3grKTxDCp0g8Tvk2Df7ptWFx2n+hxhBYVwMQSKggWuuDMFWBNA==", "dependencies": { "fast-deep-equal": "^3.1.3", "klona": "^2.0.5" @@ -4183,41 +4151,6 @@ "react": ">=16.8.0" } }, - "node_modules/@mantine/hooks": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-6.0.14.tgz", - "integrity": "sha512-cBGdTSdBDLcPScoeI12DCWFVzmT4tX+DmI9s+MOBm4IdhF4ogkLbbRgKosFbaBWNKx9WzYAUiQR/tUI5dTkJPQ==", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@mantine/styles": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/styles/-/styles-6.0.14.tgz", - "integrity": "sha512-qkqUodvVPzthmVzWQHYe/yWrc8UZcz8A5KfegK2Ps67bZzfoZs/NBVcEx6REq8HTsISjYw3jJyMRxAE3G7Ms4Q==", - "dependencies": { - "clsx": "1.1.1", - "csstype": "3.0.9" - }, - "peerDependencies": { - "@emotion/react": ">=11.9.0", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@mantine/styles/node_modules/csstype": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz", - "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==" - }, - "node_modules/@mantine/utils": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/utils/-/utils-6.0.14.tgz", - "integrity": "sha512-se+3IXJsNj4wnLMlqc7LiBe74m+JLQJ5o3wEcDtFzaJEtEt1rtw/1q3xKuROkH3xEiauhxQgzsc4gj/AslHy2A==", - "peerDependencies": { - "react": ">=16.8.0" - } - }, "node_modules/@nestjs/cli": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.5.0.tgz", @@ -5126,6 +5059,62 @@ "node": ">=14" } }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@pkgr/utils/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pkgr/utils/node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pkgr/utils/node_modules/tslib": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", + "dev": true + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -5316,9 +5305,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.3.tgz", - "integrity": "sha512-EXJysQ7J3veRECd0kZFQwYYd5sJMcq2O/m60zu1W2l3oVQ9xtub8jTOtYRE0+M2iomyG/W3Ps7+vp2kna0C27Q==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.1.tgz", + "integrity": "sha512-bgVQM4ZJ2u2CM8k1ey70o1ePFXsEzYVZoWghh6WjM8p59jQ7HxzbHW4SbnWFG7V9ig9chLawQxDTZ3xzOF8MkQ==", "engines": { "node": ">=14" } @@ -5647,20 +5636,20 @@ } }, "node_modules/@tabler/icons": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.22.0.tgz", - "integrity": "sha512-lOsGHqRPIKNARMWHHFkUUJH78C8ptQmUcDnumFBUI4YLRKFouKa7uAZL3ZfuH0HjDpOhsnWqUYZ7FhMCLcGpAQ==", + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.25.0.tgz", + "integrity": "sha512-Z+FtSZoG/CM1TMCgg7elUew2m0+qMdh5gutMhvxiIY77KIIsE6L/6fUBy+rPXWE9v7MV296fsnCvbpfgwpXupQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/codecalm" } }, "node_modules/@tabler/icons-react": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.22.0.tgz", - "integrity": "sha512-9udRxrbsbWzUETur4jmbZQIZkH7jVA2GU7YnKYS7ZCUjdOHZZEhmsfJ/Gdbzz0gSRC3mU7SDMsKejun/IBjvpQ==", + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.25.0.tgz", + "integrity": "sha512-so/amZZcvuZBBTMinReI219ek1MwlZfccGfRu7IwXEhgHftFTfyBOvTK9Kgf4vM+9+i0CcuVRoxyfQiuUwYUWg==", "dependencies": { - "@tabler/icons": "2.22.0", + "@tabler/icons": "2.25.0", "prop-types": "^15.7.2" }, "funding": { @@ -6283,9 +6272,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.12.tgz", - "integrity": "sha512-ndmBMLCgn38v3SntMeoJaIrO6tGHYKMEBohCUmw8HoLLQdRMOIGXfeYaBTLe2lsFaSB3MOK1VXscYFnmLtTSmw==", + "version": "18.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.15.tgz", + "integrity": "sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA==", "devOptional": true, "dependencies": { "@types/prop-types": "*", @@ -6294,9 +6283,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.5.tgz", - "integrity": "sha512-sRQsOS/sCLnpQhR4DSKGTtWFE3FZjpQa86KPVbhUqdYMRZ9FEFcfAytKhR/vUG2rH1oFbOOej6cuD7MFSobDRQ==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", "dev": true, "dependencies": { "@types/react": "*" @@ -7347,6 +7336,15 @@ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "dev": true }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -7409,6 +7407,18 @@ "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", "optional": true }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -7530,6 +7540,21 @@ "semver": "^7.0.0" } }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -8563,6 +8588,162 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/default-browser/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/default-browser/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -9060,15 +9241,15 @@ } }, "node_modules/eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", + "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -9080,7 +9261,7 @@ "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.0", "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "espree": "^9.6.0", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -9090,7 +9271,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -9100,9 +9280,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -9161,9 +9340,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.1.tgz", - "integrity": "sha512-QgrvtRJkmV+m4w953LS146+6RwEe5waouubFVNLBfOjXJf6MLczjymO8fOcKj9jMS8aKkTCMJqiPu2WEeFI99A==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.3.tgz", + "integrity": "sha512-Hh0wv8bUNY877+sI0BlCUlsS0TYYQqvzEwJsJJPM2WF4RnTStSnSR3zdJYa2nPOJgg3UghXi54lVyMSmpCalzA==", "dev": true, "peerDependencies": { "eslint": ">=7" @@ -9266,12 +9445,12 @@ } }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -9547,9 +9726,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -11424,6 +11603,39 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -15055,9 +15267,9 @@ } }, "node_modules/postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz", + "integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==", "dev": true, "funding": [ { @@ -15600,9 +15812,9 @@ } }, "node_modules/react-icons": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz", - "integrity": "sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz", + "integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==", "peerDependencies": { "react": "*" } @@ -15673,11 +15885,11 @@ } }, "node_modules/react-router": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.13.0.tgz", - "integrity": "sha512-Si6KnfEnJw7gUQkNa70dlpI1bul46FuSxX5t5WwlUBxE25DAz2BjVkwaK8Y2s242bQrZPXCpmwLPtIO5pv4tXg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.1.tgz", + "integrity": "sha512-U4PfgvG55LdvbQjg5Y9QRWyVxIdO1LlpYT7x+tMAxd9/vmiPuJhIwdxZuIQLN/9e3O4KFDHYfR9gzGeYMasW8g==", "dependencies": { - "@remix-run/router": "1.6.3" + "@remix-run/router": "1.7.1" }, "engines": { "node": ">=14" @@ -15687,12 +15899,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.13.0.tgz", - "integrity": "sha512-6Nqoqd7fgwxxVGdbiMHTpDHCYPq62d7Wk1Of7B82vH7ZPwwsRaIa22zRZKPPg413R5REVNiyuQPKDG1bubcOFA==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.1.tgz", + "integrity": "sha512-ssF6M5UkQjHK70fgukCJyjlda0Dgono2QGwqGvuk7D+EDGHdacEN3Yke2LTMjkrpHuFwBfDFsEjGVXBDmL+bWw==", "dependencies": { - "@remix-run/router": "1.6.3", - "react-router": "6.13.0" + "@remix-run/router": "1.7.1", + "react-router": "6.14.1" }, "engines": { "node": ">=14" @@ -16443,9 +16655,9 @@ } }, "node_modules/rollup": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.1.tgz", - "integrity": "sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==", + "version": "3.26.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.2.tgz", + "integrity": "sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -16463,6 +16675,21 @@ "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==" }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -17391,13 +17618,29 @@ "node": ">=0.10" } }, - "node_modules/tabbable": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.1.2.tgz", - "integrity": "sha512-qCN98uP7i9z0fIS4amQ5zbGBOq+OSigYeGvPy7NDk8Y9yncqDZ9pRPgfsc2PJIVM9RrJj7GIfuRgmjoUU9zTHQ==" - }, - "node_modules/tapable": { - "version": "2.2.1", + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/tabbable": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.1.2.tgz", + "integrity": "sha512-qCN98uP7i9z0fIS4amQ5zbGBOq+OSigYeGvPy7NDk8Y9yncqDZ9pRPgfsc2PJIVM9RrJj7GIfuRgmjoUU9zTHQ==" + }, + "node_modules/tapable": { + "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, @@ -17670,6 +17913,18 @@ "@popperjs/core": "^2.9.0" } }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -17797,6 +18052,18 @@ "node": ">=8" } }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-loader": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.3.tgz", @@ -18082,9 +18349,9 @@ } }, "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -18461,14 +18728,14 @@ } }, "node_modules/vite": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", - "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.4.tgz", + "integrity": "sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg==", "dev": true, "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.23", - "rollup": "^3.21.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.25", + "rollup": "^3.25.2" }, "bin": { "vite": "bin/vite.js" @@ -18476,12 +18743,16 @@ "engines": { "node": "^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -18494,6 +18765,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -18523,43 +18797,432 @@ "vite": ">=2" } }, - "node_modules/vite-plugin-eslint/node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "node_modules/vite-plugin-eslint/node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/vite-plugin-pwa": { + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.16.4.tgz", + "integrity": "sha512-lmwHFIs9zI2H9bXJld/zVTbCqCQHZ9WrpyDMqosICDV0FVnCJwniX1NMDB79HGTIZzOQkY4gSZaVTJTw6maz/Q==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "fast-glob": "^3.2.12", + "pretty-bytes": "^6.0.0", + "workbox-build": "^7.0.0", + "workbox-window": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^3.1.0 || ^4.0.0", + "workbox-build": "^7.0.0", + "workbox-window": "^7.0.0" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.13.tgz", + "integrity": "sha512-KwqFhxRFMKZINHzCqf8eKxE0XqWlAVPRxwy6rc7CbVFxzUWB2sA/s3hbMZeemPdhN3fKBkqOaFhTbS8xJXYIWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.13.tgz", + "integrity": "sha512-j7NhycJUoUAG5kAzGf4fPWfd17N6SM3o1X6MlXVqfHvs2buFraCJzos9vbeWjLxOyBKHyPOnuCuipbhvbYtTAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.13.tgz", + "integrity": "sha512-M2eZkRxR6WnWfVELHmv6MUoHbOqnzoTVSIxgtsyhm/NsgmL+uTmag/VVzdXvmahak1I6sOb1K/2movco5ikDJg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.13.tgz", + "integrity": "sha512-f5goG30YgR1GU+fxtaBRdSW3SBG9pZW834Mmhxa6terzcboz7P2R0k4lDxlkP7NYRIIdBbWp+VgwQbmMH4yV7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.13.tgz", + "integrity": "sha512-RIrxoKH5Eo+yE5BtaAIMZaiKutPhZjw+j0OCh8WdvKEKJQteacq0myZvBDLU+hOzQOZWJeDnuQ2xgSScKf1Ovw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.13.tgz", + "integrity": "sha512-AfRPhHWmj9jGyLgW/2FkYERKmYR+IjYxf2rtSLmhOrPGFh0KCETFzSjx/JX/HJnvIqHt/DRQD/KAaVsUKoI3Xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.13.tgz", + "integrity": "sha512-pGzWWZJBInhIgdEwzn8VHUBang8UvFKsvjDkeJ2oyY5gZtAM6BaxK0QLCuZY+qoj/nx/lIaItH425rm/hloETA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.13.tgz", + "integrity": "sha512-4iMxLRMCxGyk7lEvkkvrxw4aJeC93YIIrfbBlUJ062kilUUnAiMb81eEkVvCVoh3ON283ans7+OQkuy1uHW+Hw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.13.tgz", + "integrity": "sha512-hCzZbVJEHV7QM77fHPv2qgBcWxgglGFGCxk6KfQx6PsVIdi1u09X7IvgE9QKqm38OpkzaAkPnnPqwRsltvLkIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.13.tgz", + "integrity": "sha512-I3OKGbynl3AAIO6onXNrup/ttToE6Rv2XYfFgLK/wnr2J+1g+7k4asLrE+n7VMhaqX+BUnyWkCu27rl+62Adug==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.13.tgz", + "integrity": "sha512-8pcKDApAsKc6WW51ZEVidSGwGbebYw2qKnO1VyD8xd6JN0RN6EUXfhXmDk9Vc4/U3Y4AoFTexQewQDJGsBXBpg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.13.tgz", + "integrity": "sha512-6GU+J1PLiVqWx8yoCK4Z0GnfKyCGIH5L2KQipxOtbNPBs+qNDcMJr9euxnyJ6FkRPyMwaSkjejzPSISD9hb+gg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.13.tgz", + "integrity": "sha512-pfn/OGZ8tyR8YCV7MlLl5hAit2cmS+j/ZZg9DdH0uxdCoJpV7+5DbuXrR+es4ayRVKIcfS9TTMCs60vqQDmh+w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.13.tgz", + "integrity": "sha512-aIbhU3LPg0lOSCfVeGHbmGYIqOtW6+yzO+Nfv57YblEK01oj0mFMtvDJlOaeAZ6z0FZ9D13oahi5aIl9JFphGg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.13.tgz", + "integrity": "sha512-Pct1QwF2sp+5LVi4Iu5Y+6JsGaV2Z2vm4O9Dd7XZ5tKYxEHjFtb140fiMcl5HM1iuv6xXO8O1Vrb1iJxHlv8UA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.13.tgz", + "integrity": "sha512-zTrIP0KzYP7O0+3ZnmzvUKgGtUvf4+piY8PIO3V8/GfmVd3ZyHJGz7Ht0np3P1wz+I8qJ4rjwJKqqEAbIEPngA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.13.tgz", + "integrity": "sha512-I6zs10TZeaHDYoGxENuksxE1sxqZpCp+agYeW039yqFwh3MgVvdmXL5NMveImOC6AtpLvE4xG5ujVic4NWFIDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.13.tgz", + "integrity": "sha512-W5C5nczhrt1y1xPG5bV+0M12p2vetOGlvs43LH8SopQ3z2AseIROu09VgRqydx5qFN7y9qCbpgHLx0kb0TcW7g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.13.tgz", + "integrity": "sha512-X/xzuw4Hzpo/yq3YsfBbIsipNgmsm8mE/QeWbdGdTTeZ77fjxI2K0KP3AlhZ6gU3zKTw1bKoZTuKLnqcJ537qw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.13.tgz", + "integrity": "sha512-4CGYdRQT/ILd+yLLE5i4VApMPfGE0RPc/wFQhlluDQCK09+b4JDbxzzjpgQqTPrdnP7r5KUtGVGZYclYiPuHrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.13.tgz", + "integrity": "sha512-D+wKZaRhQI+MUGMH+DbEr4owC2D7XnF+uyGiZk38QbgzLcofFqIOwFs7ELmIeU45CQgfHNy9Q+LKW3cE8g37Kg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.13.tgz", + "integrity": "sha512-iVl6lehAfJS+VmpF3exKpNQ8b0eucf5VWfzR8S7xFve64NBNz2jPUgx1X93/kfnkfgP737O+i1k54SVQS7uVZA==", + "cpu": [ + "x64" + ], "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": ">=12" } }, - "node_modules/vite-plugin-pwa": { - "version": "0.16.4", - "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.16.4.tgz", - "integrity": "sha512-lmwHFIs9zI2H9bXJld/zVTbCqCQHZ9WrpyDMqosICDV0FVnCJwniX1NMDB79HGTIZzOQkY4gSZaVTJTw6maz/Q==", + "node_modules/vite/node_modules/esbuild": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.13.tgz", + "integrity": "sha512-vhg/WR/Oiu4oUIkVhmfcc23G6/zWuEQKFS+yiosSHe4aN6+DQRXIfeloYGibIfVhkr4wyfuVsGNLr+sQU1rWWw==", "dev": true, - "dependencies": { - "debug": "^4.3.4", - "fast-glob": "^3.2.12", - "pretty-bytes": "^6.0.0", - "workbox-build": "^7.0.0", - "workbox-window": "^7.0.0" + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" + "node": ">=12" }, - "peerDependencies": { - "vite": "^3.1.0 || ^4.0.0", - "workbox-build": "^7.0.0", - "workbox-window": "^7.0.0" + "optionalDependencies": { + "@esbuild/android-arm": "0.18.13", + "@esbuild/android-arm64": "0.18.13", + "@esbuild/android-x64": "0.18.13", + "@esbuild/darwin-arm64": "0.18.13", + "@esbuild/darwin-x64": "0.18.13", + "@esbuild/freebsd-arm64": "0.18.13", + "@esbuild/freebsd-x64": "0.18.13", + "@esbuild/linux-arm": "0.18.13", + "@esbuild/linux-arm64": "0.18.13", + "@esbuild/linux-ia32": "0.18.13", + "@esbuild/linux-loong64": "0.18.13", + "@esbuild/linux-mips64el": "0.18.13", + "@esbuild/linux-ppc64": "0.18.13", + "@esbuild/linux-riscv64": "0.18.13", + "@esbuild/linux-s390x": "0.18.13", + "@esbuild/linux-x64": "0.18.13", + "@esbuild/netbsd-x64": "0.18.13", + "@esbuild/openbsd-x64": "0.18.13", + "@esbuild/sunos-x64": "0.18.13", + "@esbuild/win32-arm64": "0.18.13", + "@esbuild/win32-ia32": "0.18.13", + "@esbuild/win32-x64": "0.18.13" } }, "node_modules/vscode-oniguruma": { @@ -19867,16 +20530,16 @@ "version": "4.4.0", "license": "MIT", "dependencies": { - "@emotion/react": "^11.10.6", - "@mantine/core": "^6.0.14", - "@mantine/dropzone": "6.0.14", - "@mantine/form": "^6.0.14", - "@mantine/hooks": "^6.0.14", - "@mantine/modals": "^6.0.14", - "@mantine/notifications": "^6.0.14", - "@mantine/prism": "^6.0.14", - "@mantine/tiptap": "^6.0.14", - "@tabler/icons-react": "^2.22.0", + "@emotion/react": "^11.11.1", + "@mantine/core": "^6.0.16", + "@mantine/dropzone": "6.0.16", + "@mantine/form": "^6.0.16", + "@mantine/hooks": "^6.0.16", + "@mantine/modals": "^6.0.16", + "@mantine/notifications": "^6.0.16", + "@mantine/prism": "^6.0.16", + "@mantine/tiptap": "^6.0.16", + "@tabler/icons-react": "^2.25.0", "@tiptap/extension-code-block-lowlight": "^2.0.3", "@tiptap/extension-link": "^2.0.3", "@tiptap/pm": "^2.0.3", @@ -19886,102 +20549,393 @@ "lowlight": "^2.9.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-icons": "^4.8.0", - "react-router-dom": "^6.13.0", + "react-icons": "^4.10.1", + "react-router-dom": "^6.14.1", "uuid": "^9.0.0", "zod": "^3.21.4", "zustand": "^4.3.9" }, "devDependencies": { - "@types/react": "^18.0.26", - "@types/react-dom": "^18.0.9", + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", "@types/uuid": "^9.0.2", - "@typescript-eslint/eslint-plugin": "^5.54.0", - "@typescript-eslint/parser": "^5.54.0", - "@vitejs/plugin-react-swc": "^3.0.0", - "eslint": "^8.35.0", - "eslint-config-prettier": "^8.6.0", - "eslint-plugin-prettier": "^4.2.1", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react-swc": "^3.3.2", + "eslint": "^8.45.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.1", - "prettier": "^2.8.4", - "typescript": "^4.9.3", - "vite": "^4.0.0", + "eslint-plugin-react-refresh": "^0.4.3", + "prettier": "^3.0.0", + "typescript": "^5.1.6", + "vite": "^4.4.4", "vite-plugin-eslint": "^1.8.1", "vite-plugin-pwa": "^0.16.4" } }, + "packages/web/node_modules/@mantine/core": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-6.0.16.tgz", + "integrity": "sha512-ai7HfWo214JLmf+QNEmFfTaBgxreMpY/ZK7dIDNkFfCmDAWjFxwCSc7flh8NHX2ZJN5uTc82p2i0MLJ/GZ5xrA==", + "dependencies": { + "@floating-ui/react": "^0.19.1", + "@mantine/styles": "6.0.16", + "@mantine/utils": "6.0.16", + "@radix-ui/react-scroll-area": "1.0.2", + "react-remove-scroll": "^2.5.5", + "react-textarea-autosize": "8.3.4" + }, + "peerDependencies": { + "@mantine/hooks": "6.0.16", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "packages/web/node_modules/@mantine/dropzone": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-6.0.16.tgz", + "integrity": "sha512-zVmJFPesI4NyLvjvhM77WwE+AO+ixPq934fjrtCvHTtFRVrbuuUa+tOk6LNPX0grQLOZMb0fnlZXy8KH03ES+w==", + "dependencies": { + "@mantine/utils": "6.0.16", + "react-dropzone": "14.2.3" + }, + "peerDependencies": { + "@mantine/core": "6.0.16", + "@mantine/hooks": "6.0.16", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "packages/web/node_modules/@mantine/hooks": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-6.0.16.tgz", + "integrity": "sha512-DnfMYSTSjYxbQJ80TzKHO5gRXGTIQKxBnRQVc+n4RANTwgWMiwEmxIwqRjbulfLzIhEvplskhqGgElunIAfw7g==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, "packages/web/node_modules/@mantine/modals": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-6.0.14.tgz", - "integrity": "sha512-54kOKLfbsp3ox51DYTgMJgBQ3QQ6qaTPZBdABaBPNJcJ1mwIFUQ457+evegwf2r4sKqaT2edTMmumFoa0YXyVg==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-6.0.16.tgz", + "integrity": "sha512-RMDp5jZTrbXgMJN3aPBt82LsIL91xepQDq/zmtLnj2JJnDYTKZarvpOJPvUO/Ewyr6GYJ3iNbcXkV0w0CqCNGg==", "dependencies": { - "@mantine/utils": "6.0.14" + "@mantine/utils": "6.0.16" }, "peerDependencies": { - "@mantine/core": "6.0.14", - "@mantine/hooks": "6.0.14", + "@mantine/core": "6.0.16", + "@mantine/hooks": "6.0.16", "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "packages/web/node_modules/@mantine/notifications": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-6.0.14.tgz", - "integrity": "sha512-ElzIVojgAplm9Gtq1qZWR/kjGupttRq8ctTUYmANV8yyXcbpErFr45RlYjDgJs2klQcZid3Pq7hVsjGKLF2MQw==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-6.0.16.tgz", + "integrity": "sha512-KqlPW51sxgQoJmIC2lEWMVlwPqy04D35iRMkCSget8aNgzk0K5csJppXo6qwMFn2GHKVGXFKJMBUp06IXQbiig==", "dependencies": { - "@mantine/utils": "6.0.14", + "@mantine/utils": "6.0.16", "react-transition-group": "4.4.2" }, "peerDependencies": { - "@mantine/core": "6.0.14", - "@mantine/hooks": "6.0.14", + "@mantine/core": "6.0.16", + "@mantine/hooks": "6.0.16", "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "packages/web/node_modules/@mantine/prism": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/prism/-/prism-6.0.14.tgz", - "integrity": "sha512-w7isUgEintOY3d6m0t31oA+2YvgI78gXBLOCdnGgBiE4kbYYnXUUBo5oTS6Otro1v+pXWZ5rZqPZlDT21jSxSA==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/prism/-/prism-6.0.16.tgz", + "integrity": "sha512-bWnwRMA7fBfSMpZO2YHIsJ4yt3JTZmPp1nQ62JKpey7rfCJMMuzuCXagCHZthEChZZoQnwmndr4agEzuNIIxmA==", "dependencies": { - "@mantine/utils": "6.0.14", + "@mantine/utils": "6.0.16", "prism-react-renderer": "^1.2.1" }, "peerDependencies": { - "@mantine/core": "6.0.14", - "@mantine/hooks": "6.0.14", + "@mantine/core": "6.0.16", + "@mantine/hooks": "6.0.16", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "packages/web/node_modules/@mantine/styles": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/styles/-/styles-6.0.16.tgz", + "integrity": "sha512-0rDE3BzJOKVdPAbnL3XokVWNYgo1z323UgAURdB+WvzwZY7PhblwvrxUDwvvV77qCUENg2vU0LKHqTQD1DDLTg==", + "dependencies": { + "clsx": "1.1.1", + "csstype": "3.0.9" + }, + "peerDependencies": { + "@emotion/react": ">=11.9.0", "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "packages/web/node_modules/@mantine/tiptap": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@mantine/tiptap/-/tiptap-6.0.14.tgz", - "integrity": "sha512-J7hhgLcCAN4snc7PKlU6ou0/Tvf0bWhexR/vExoNdYLvnCd7JfdDGWL+1O1SaXd2kDgWCmxzLTAiXoS71+l/Pw==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/tiptap/-/tiptap-6.0.16.tgz", + "integrity": "sha512-W/289lRW7AoNOh4/1WX31K0z+Uk9lJpZyC+m3TuywPSJCINU2ETmj1r+RZl3szf2hLCt5FhSspIAcPgkA2LYQw==", "dependencies": { - "@mantine/utils": "6.0.14" + "@mantine/utils": "6.0.16" }, "peerDependencies": { - "@mantine/core": "6.0.14", - "@mantine/hooks": "6.0.14", + "@mantine/core": "6.0.16", + "@mantine/hooks": "6.0.16", "@tabler/icons-react": ">=2.1.0", "@tiptap/extension-link": "^2.0.0-beta.202", "@tiptap/react": "^2.0.0-beta.202", "react": ">=16.8.0" } }, - "packages/web/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "packages/web/node_modules/@mantine/utils": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@mantine/utils/-/utils-6.0.16.tgz", + "integrity": "sha512-UFel9DbifL3zS8pTJlr6GfwGd6464OWXCJdUq0oLydgimbC1VV2PnptBr6FMwIpPVcxouLOtY1cChzwFH95PSA==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "packages/web/node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.0.0.tgz", + "integrity": "sha512-xuv6ghKGoiq856Bww/yVYnXGsKa588kY3M0XK7uUW/3fJNNULKRfZfSBkMTSpqGG/8ZCXCadfh8G/z/B4aqS/A==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.0", + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/type-utils": "6.0.0", + "@typescript-eslint/utils": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.5.0", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/web/node_modules/@typescript-eslint/parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.0.0.tgz", + "integrity": "sha512-TNaufYSPrr1U8n+3xN+Yp9g31vQDJqhXzzPSHfQDLcaO4tU+mCfODPxCwf4H530zo7aUBE3QIdxCXamEnG04Tg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/typescript-estree": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/web/node_modules/@typescript-eslint/scope-manager": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.0.0.tgz", + "integrity": "sha512-o4q0KHlgCZTqjuaZ25nw5W57NeykZT9LiMEG4do/ovwvOcPnDO1BI5BQdCsUkjxFyrCL0cSzLjvIMfR9uo7cWg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/web/node_modules/@typescript-eslint/type-utils": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.0.0.tgz", + "integrity": "sha512-ah6LJvLgkoZ/pyJ9GAdFkzeuMZ8goV6BH7eC9FPmojrnX9yNCIsfjB+zYcnex28YO3RFvBkV6rMV6WpIqkPvoQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.0.0", + "@typescript-eslint/utils": "6.0.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/web/node_modules/@typescript-eslint/types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.0.0.tgz", + "integrity": "sha512-Zk9KDggyZM6tj0AJWYYKgF0yQyrcnievdhG0g5FqyU3Y2DRxJn4yWY21sJC0QKBckbsdKKjYDV2yVrrEvuTgxg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/web/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.0.0.tgz", + "integrity": "sha512-2zq4O7P6YCQADfmJ5OTDQTP3ktajnXIRrYAtHM9ofto/CJZV3QfJ89GEaM2BNGeSr1KgmBuLhEkz5FBkS2RQhQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.0", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/web/node_modules/@typescript-eslint/utils": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.0.0.tgz", + "integrity": "sha512-SOr6l4NB6HE4H/ktz0JVVWNXqCJTOo/mHnvIte1ZhBQ0Cvd04x5uKZa3zT6tiodL06zf5xxdK8COiDvPnQ27JQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.3.0", + "@types/json-schema": "^7.0.11", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/typescript-estree": "6.0.0", + "eslint-scope": "^5.1.1", + "semver": "^7.5.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "packages/web/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.0.0.tgz", + "integrity": "sha512-cvJ63l8c0yXdeT5POHpL0Q1cZoRcmRKFCtSjNGJxPkcP571EfZMcNbzWAc7oK3D1dRzm/V5EwtkANTZxqvuuUA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.0.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/web/node_modules/csstype": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz", + "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==" + }, + "packages/web/node_modules/eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "packages/web/node_modules/prettier": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", "dev": true, "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=4.2.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } } } diff --git a/package.json b/package.json index eed4085..e482355 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "packages/*" ], "devDependencies": { - "lerna": "^7.0.2" + "lerna": "^7.0.2", + "typescript": "^5.1.6" }, "dependencies": { "immer": "^10.0.2" diff --git a/packages/web/package.json b/packages/web/package.json index 78f3d88..5a2cf35 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -51,16 +51,16 @@ "dist" ], "dependencies": { - "@emotion/react": "^11.10.6", - "@mantine/core": "^6.0.14", - "@mantine/dropzone": "6.0.14", - "@mantine/form": "^6.0.14", - "@mantine/hooks": "^6.0.14", - "@mantine/modals": "^6.0.14", - "@mantine/notifications": "^6.0.14", - "@mantine/prism": "^6.0.14", - "@mantine/tiptap": "^6.0.14", - "@tabler/icons-react": "^2.22.0", + "@emotion/react": "^11.11.1", + "@mantine/core": "^6.0.16", + "@mantine/dropzone": "6.0.16", + "@mantine/form": "^6.0.16", + "@mantine/hooks": "^6.0.16", + "@mantine/modals": "^6.0.16", + "@mantine/notifications": "^6.0.16", + "@mantine/prism": "^6.0.16", + "@mantine/tiptap": "^6.0.16", + "@tabler/icons-react": "^2.25.0", "@tiptap/extension-code-block-lowlight": "^2.0.3", "@tiptap/extension-link": "^2.0.3", "@tiptap/pm": "^2.0.3", @@ -70,27 +70,27 @@ "lowlight": "^2.9.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-icons": "^4.8.0", - "react-router-dom": "^6.13.0", + "react-icons": "^4.10.1", + "react-router-dom": "^6.14.1", "uuid": "^9.0.0", "zod": "^3.21.4", "zustand": "^4.3.9" }, "devDependencies": { - "@types/react": "^18.0.26", - "@types/react-dom": "^18.0.9", + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", "@types/uuid": "^9.0.2", - "@typescript-eslint/eslint-plugin": "^5.54.0", - "@typescript-eslint/parser": "^5.54.0", - "@vitejs/plugin-react-swc": "^3.0.0", - "eslint": "^8.35.0", - "eslint-config-prettier": "^8.6.0", - "eslint-plugin-prettier": "^4.2.1", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react-swc": "^3.3.2", + "eslint": "^8.45.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.1", - "prettier": "^2.8.4", - "typescript": "^4.9.3", - "vite": "^4.0.0", + "eslint-plugin-react-refresh": "^0.4.3", + "prettier": "^3.0.0", + "typescript": "^5.1.6", + "vite": "^4.4.4", "vite-plugin-eslint": "^1.8.1", "vite-plugin-pwa": "^0.16.4" } diff --git a/packages/web/src/changelog/index.ts b/packages/web/src/changelog/index.ts index 5e74305..15b6432 100644 --- a/packages/web/src/changelog/index.ts +++ b/packages/web/src/changelog/index.ts @@ -1,16 +1,19 @@ import { ReactNode } from "react"; import v4_4_0 from "./v4-4-0"; +import v4_4_1 from "./v4-4-1"; export type ChangelogEntrySection = { label: ReactNode; items: ReactNode[]; + description?: ReactNode; }; export type ChangelogEntry = { version: string; date: Date; sections: ChangelogEntrySection[]; + description?: ReactNode; }; // First entry is the latest version -export const changelog = [v4_4_0]; +export const changelog = [v4_4_1, v4_4_0]; diff --git a/packages/web/src/changelog/v4-4-1.ts b/packages/web/src/changelog/v4-4-1.ts new file mode 100644 index 0000000..7e05365 --- /dev/null +++ b/packages/web/src/changelog/v4-4-1.ts @@ -0,0 +1,21 @@ +import { ChangelogEntry } from "."; +import { CHANGELOG_SECTION } from "../config/constants"; + +const v4_4_1: ChangelogEntry = { + version: "4.4.1 - Performance Update", + date: new Date("july 15 2023"), + description: + "Although performance was not an issue before, I saw many easy opportunities to improve it. This update contains many small improvements that should make the app feel more responsive and less resource intensive.", + sections: [ + { + label: CHANGELOG_SECTION.IMPROVEMENTS, + items: [ + "Further improved state management", + "Significantly reduced bundle size by separating dependencies from the main bundle into separate chunks. They will only be loaded when needed.", + "Updated all project dependencies", + ], + }, + ], +}; + +export default v4_4_1; diff --git a/packages/web/src/components/About.tsx b/packages/web/src/components/About/About.tsx similarity index 97% rename from packages/web/src/components/About.tsx rename to packages/web/src/components/About/About.tsx index 1c57efa..8efb70e 100644 --- a/packages/web/src/components/About.tsx +++ b/packages/web/src/components/About/About.tsx @@ -1,5 +1,5 @@ import { Anchor, Stack, Text } from "@mantine/core"; -import { DISCORD_SERVER_INVITE } from "../config/constants"; +import { DISCORD_SERVER_INVITE } from "../../config/constants"; const About = () => { return ( diff --git a/packages/web/src/components/About/AboutButton.tsx b/packages/web/src/components/About/AboutButton.tsx new file mode 100644 index 0000000..659b02e --- /dev/null +++ b/packages/web/src/components/About/AboutButton.tsx @@ -0,0 +1,32 @@ +import { Modal, Text } from "@mantine/core"; +import { useDisclosure } from "@mantine/hooks"; +import { FaRegQuestionCircle } from "react-icons/fa"; +import TippedActionIcon from "../common/TippedActionIcon"; +import About from "./About"; + +const AboutButton = () => { + const [showAbout, { open: openAbout, close: closeAbout }] = useDisclosure(); + + return ( + <> + + + + About GPT Turbo Web} + > + + + + ); +}; + +export default AboutButton; diff --git a/packages/web/src/components/AddConversation.tsx b/packages/web/src/components/AddConversation.tsx index 0353175..f82dc4d 100644 --- a/packages/web/src/components/AddConversation.tsx +++ b/packages/web/src/components/AddConversation.tsx @@ -6,7 +6,13 @@ import { Card, createStyles, } from "@mantine/core"; -import AddConversationForm from "./AddConversationForm"; +import React from "react"; +import { ConversationFormValues } from "../contexts/ConversationFormContext"; +import { addConversation } from "../store/actions/conversations/addConversation"; +import { setActiveConversation } from "../store/actions/conversations/setActiveConversation"; +import { addPersistedConversationId } from "../store/actions/persistence/addPersistedConversationId"; +import ConversationForm from "./forms/ConversationForm/ConversationForm"; +import { useGetFunction } from "../store/hooks/callableFunctions/useGetFunction"; const useStyles = createStyles((theme) => ({ card: { @@ -23,6 +29,32 @@ const useStyles = createStyles((theme) => ({ const AddConversation = () => { const { classes } = useStyles(); + const getFunction = useGetFunction(); + + const onSubmit = React.useCallback( + ({ + save, + headers, + proxy, + functionIds, + ...values + }: ConversationFormValues) => { + const newConversation = addConversation(values, { headers, proxy }); + setActiveConversation(newConversation.id, true); + + for (const functionId of functionIds) { + const callableFunction = getFunction(functionId); + if (callableFunction) { + newConversation.addFunction(callableFunction); + } + } + + if (save) { + addPersistedConversationId(newConversation.id); + } + }, + [getFunction] + ); return ( @@ -38,7 +70,7 @@ const AddConversation = () => { New Conversation - + diff --git a/packages/web/src/components/AddConversationForm.tsx b/packages/web/src/components/AddConversationForm.tsx deleted file mode 100644 index 624756f..0000000 --- a/packages/web/src/components/AddConversationForm.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from "react"; -import ConversationForm from "./ConversationForm"; -import { ConversationFormValues } from "../contexts/ConversationFormContext"; -import useCallableFunctions from "../hooks/useCallableFunctions"; -import { addConversation } from "../store/actions/conversations/addConversation"; -import { setActiveConversation } from "../store/actions/conversations/setActiveConversation"; -import { addPersistedConversationId } from "../store/actions/persistence/addPersistedConversationId"; - -const AddConversationForm = () => { - const { getCallableFunction } = useCallableFunctions(); - - const onSubmit = React.useCallback( - ({ - save, - headers, - proxy, - functionIds, - ...values - }: ConversationFormValues) => { - const newConversation = addConversation(values, { headers, proxy }); - setActiveConversation(newConversation.id, true); - - for (const functionId of functionIds) { - const callableFunction = getCallableFunction(functionId); - newConversation.addFunction(callableFunction); - } - - if (save) { - addPersistedConversationId(newConversation.id); - } - }, - [getCallableFunction] - ); - - return ; -}; - -export default AddConversationForm; diff --git a/packages/web/src/components/AppSettings.tsx b/packages/web/src/components/AppSettings/AppSettings.tsx similarity index 91% rename from packages/web/src/components/AppSettings.tsx rename to packages/web/src/components/AppSettings/AppSettings.tsx index 802ef5a..5637a10 100644 --- a/packages/web/src/components/AppSettings.tsx +++ b/packages/web/src/components/AppSettings/AppSettings.tsx @@ -9,8 +9,8 @@ import { useMantineColorScheme, } from "@mantine/core"; import AppStorageUsage from "./AppStorageUsage"; -import { useAppStore } from "../store"; -import { toggleShowUsage } from "../store/actions/appSettings/toggleShowUsage"; +import { useAppStore } from "../../store"; +import { toggleShowUsage } from "../../store/actions/appSettings/toggleShowUsage"; import AppSettingsDangerZone from "./AppSettingsDangerZone"; const AppSettings = () => { diff --git a/packages/web/src/components/AppSettingsDangerZone.tsx b/packages/web/src/components/AppSettings/AppSettingsDangerZone.tsx similarity index 73% rename from packages/web/src/components/AppSettingsDangerZone.tsx rename to packages/web/src/components/AppSettings/AppSettingsDangerZone.tsx index 92b4e01..965485b 100644 --- a/packages/web/src/components/AppSettingsDangerZone.tsx +++ b/packages/web/src/components/AppSettings/AppSettingsDangerZone.tsx @@ -1,12 +1,12 @@ import { Group, SimpleGrid, Stack, Text } from "@mantine/core"; -import { removeAllCallableFunctions } from "../store/actions/callableFunctions/removeAllCallableFunctions"; -import { removeAllConversations } from "../store/actions/conversations/removeAllConversations"; -import InlineConfirmButton from "./InlineConfirmButton"; +import { removeAllCallableFunctions } from "../../store/actions/callableFunctions/removeAllCallableFunctions"; +import { removeAllConversations } from "../../store/actions/conversations/removeAllConversations"; +import InlineConfirmButton from "../common/InlineConfirmButton"; import React from "react"; -import { resetCallableFunctionWarnings } from "../store/actions/callableFunctions/resetCallableFunctionWarnings"; -import { resetDefaultSettings } from "../store/actions/defaultConversationSettings/resetDefaultSettings"; -import { removeAllSavedContexts } from "../store/actions/savedContexts/removeAllSavedContexts"; -import { removeAllSavedPrompts } from "../store/actions/savedPrompts/removeAllSavedPrompts"; +import { resetCallableFunctionWarnings } from "../../store/actions/callableFunctions/resetCallableFunctionWarnings"; +import { resetDefaultSettings } from "../../store/actions/defaultConversationSettings/resetDefaultSettings"; +import { removeAllSavedContexts } from "../../store/actions/savedContexts/removeAllSavedContexts"; +import { removeAllSavedPrompts } from "../../store/actions/savedPrompts/removeAllSavedPrompts"; const AppSettingsDangerZone = () => { const actions = React.useMemo( diff --git a/packages/web/src/components/AppStorageUsage.tsx b/packages/web/src/components/AppSettings/AppStorageUsage.tsx similarity index 98% rename from packages/web/src/components/AppStorageUsage.tsx rename to packages/web/src/components/AppSettings/AppStorageUsage.tsx index aeba139..7b236ea 100644 --- a/packages/web/src/components/AppStorageUsage.tsx +++ b/packages/web/src/components/AppSettings/AppStorageUsage.tsx @@ -8,7 +8,7 @@ import { useMantineTheme, } from "@mantine/core"; import React from "react"; -import { STORAGE_PERSISTENCE_KEY } from "../config/constants"; +import { STORAGE_PERSISTENCE_KEY } from "../../config/constants"; interface StorageUsage { label: string; diff --git a/packages/web/src/components/CallableFunctionCard.tsx b/packages/web/src/components/CallableFunctionCard.tsx deleted file mode 100644 index 1264024..0000000 --- a/packages/web/src/components/CallableFunctionCard.tsx +++ /dev/null @@ -1,215 +0,0 @@ -import { - ActionIcon, - Card, - CardProps, - Code, - Group, - Menu, - Stack, - Text, - Title, -} from "@mantine/core"; -import React from "react"; -import { CallableFunction } from "gpt-turbo"; -import useCallableFunctions from "../hooks/useCallableFunctions"; -import { - BiDotsVerticalRounded, - BiDuplicate, - BiEdit, - BiExport, -} from "react-icons/bi"; -import { useNavigate } from "react-router-dom"; -import { modals } from "@mantine/modals"; -import { BsTrash } from "react-icons/bs"; -import { callableFunctionExportschema } from "../entities/callableFunctionExport"; -import { useAppStore } from "../store"; -import { addCallableFunction } from "../store/actions/callableFunctions/addCallableFunction"; -import { deleteCallableFunction } from "../store/actions/callableFunctions/deleteCallableFunction"; - -type CallableFunctionCardProps = Omit & { - fn: CallableFunction; -}; - -const CallableFunctionCard = ({ - fn, - ...cardProps -}: CallableFunctionCardProps) => { - const navigate = useNavigate(); - const callableFunctions = useAppStore((state) => state.callableFunctions); - const { getCallableFunctionDisplayName, getCallableFunctionCode } = - useCallableFunctions(); - - const signature = React.useMemo(() => { - const parameters = [ - ...fn.requiredParameters, - ...fn.optionalParameters, - ].map((parameter) => ({ - name: parameter.name, - type: parameter.type, - required: fn.requiredParameters.includes(parameter), - })); - - let signature = `${fn.name}(`; - signature += parameters - .map(({ name, required, type }) => { - return `${name}${required ? "" : "?"}: ${type}`; - }) - .join(", "); - signature += ")"; - return signature; - }, [fn.name, fn.optionalParameters, fn.requiredParameters]); - - const onEdit = React.useCallback( - () => navigate(`/functions/edit/${fn.id}`), - [fn.id, navigate] - ); - - const onDuplicate = React.useCallback(() => { - const displayName = getCallableFunctionDisplayName(fn.id); - - const copyDisplayName = (() => { - let i = 1; - let copy = `${displayName} (${i})`; - while ( - callableFunctions.some( - (fn) => getCallableFunctionDisplayName(fn.id) === copy - ) - ) { - copy = `${displayName} (${i++})`; - } - return copy; - })(); - - const copyName = (() => { - let i = 1; - let copy = `${fn.name}${i}`; - while (callableFunctions.some((fn) => fn.name === copy)) { - copy = `${fn.name}${i++}`; - } - return copy; - })(); - - modals.openConfirmModal({ - title: `Duplicate ${displayName}?`, - centered: true, - children: ( - - This will create a new function with the same config and - code, but the display name and function name will appear as{" "} - - {copyDisplayName} - {" "} - and{" "} - - {copyName} - - - ), - labels: { confirm: "Duplicate function", cancel: "Cancel" }, - onConfirm: () => { - addCallableFunction( - { - ...fn.toJSON(), - id: undefined, - name: copyName, - }, - copyDisplayName, - getCallableFunctionCode(fn.id) - ); - }, - }); - }, [ - callableFunctions, - fn, - getCallableFunctionCode, - getCallableFunctionDisplayName, - ]); - - const onDelete = React.useCallback(() => { - const displayName = getCallableFunctionDisplayName(fn.id); - modals.openConfirmModal({ - title: `Delete ${displayName}?`, - centered: true, - children: ( - - Are you sure you want to delete {displayName}? This cannot - be undone. - - ), - labels: { confirm: "Delete function", cancel: "Cancel" }, - confirmProps: { color: "red" }, - onConfirm: () => { - deleteCallableFunction(fn.id); - }, - }); - }, [fn.id, getCallableFunctionDisplayName]); - - const onExport = React.useCallback(() => { - const data = JSON.stringify( - callableFunctionExportschema.parse({ - callableFunction: fn.toJSON(), - code: getCallableFunctionCode(fn.id), - displayName: getCallableFunctionDisplayName(fn.id), - }) - ); - const blob = new Blob([data], { type: "application/json" }); - const url = URL.createObjectURL(blob); - const a = document.createElement("a"); - a.href = url; - a.download = `${fn.id}.json`; - a.click(); - URL.revokeObjectURL(url); - }, [fn, getCallableFunctionCode, getCallableFunctionDisplayName]); - - return ( - - - - - {getCallableFunctionDisplayName(fn.id)} - - - - - - - - - } - > - Edit - - } - > - Duplicate - - }> - Export - - } - color="red" - > - Delete - - - - - - - - {fn.description && {fn.description}} - {signature} - - - - ); -}; - -export default CallableFunctionCard; diff --git a/packages/web/src/components/CallableFunctionCard/CallableFunctionCard.tsx b/packages/web/src/components/CallableFunctionCard/CallableFunctionCard.tsx new file mode 100644 index 0000000..38f0608 --- /dev/null +++ b/packages/web/src/components/CallableFunctionCard/CallableFunctionCard.tsx @@ -0,0 +1,64 @@ +import { + Card, + CardProps, + Code, + Group, + Stack, + Text, + Title, +} from "@mantine/core"; +import React from "react"; +import { CallableFunction } from "gpt-turbo"; +import CallableFunctionCardMenu from "./CallableFunctionCardMenu"; +import { useGetFunctionDisplayName } from "../../store/hooks/callableFunctions/useGetFunctionDisplayName"; + +type CallableFunctionCardProps = Omit & { + fn: CallableFunction; +}; + +const CallableFunctionCard = ({ + fn, + ...cardProps +}: CallableFunctionCardProps) => { + const getFunctionDisplayName = useGetFunctionDisplayName(); + const signature = React.useMemo(() => { + const parameters = [ + ...fn.requiredParameters, + ...fn.optionalParameters, + ].map((parameter) => ({ + name: parameter.name, + type: parameter.type, + required: fn.requiredParameters.includes(parameter), + })); + + let signature = `${fn.name}(`; + signature += parameters + .map(({ name, required, type }) => { + return `${name}${required ? "" : "?"}: ${type}`; + }) + .join(", "); + signature += ")"; + return signature; + }, [fn.name, fn.optionalParameters, fn.requiredParameters]); + + return ( + + + + + {getFunctionDisplayName(fn.id) ?? "[N/A]"} + + + + + + + {fn.description && {fn.description}} + {signature} + + + + ); +}; + +export default CallableFunctionCard; diff --git a/packages/web/src/components/CallableFunctionCard/CallableFunctionCardMenu.tsx b/packages/web/src/components/CallableFunctionCard/CallableFunctionCardMenu.tsx new file mode 100644 index 0000000..a82063d --- /dev/null +++ b/packages/web/src/components/CallableFunctionCard/CallableFunctionCardMenu.tsx @@ -0,0 +1,86 @@ +import { Menu, ActionIcon } from "@mantine/core"; +import React from "react"; +import { + BiDotsVerticalRounded, + BiEdit, + BiDuplicate, + BiExport, +} from "react-icons/bi"; +import { BsTrash } from "react-icons/bs"; +import { useNavigate } from "react-router-dom"; +import { callableFunctionExportschema } from "../../entities/callableFunctionExport"; +import { useAppStore } from "../../store"; +import { deleteCallableFunction } from "../../store/actions/callableFunctions/deleteCallableFunction"; +import { duplicateCallableFunction } from "../../store/actions/callableFunctions/duplicateCallableFunction"; +import { useGetFunctionDisplayName } from "../../store/hooks/callableFunctions/useGetFunctionDisplayName"; +import { useGetFunctionCode } from "../../store/hooks/callableFunctions/useGetFunctionCode"; + +interface CallableFunctionCardMenuProps { + id: string; +} + +const CallableFunctionCardMenu = ({ id }: CallableFunctionCardMenuProps) => { + const navigate = useNavigate(); + const fn = useAppStore((state) => + state.callableFunctions.find((fn) => fn.id === id) + ); + const getFunctionDisplayName = useGetFunctionDisplayName(); + const getFunctionCode = useGetFunctionCode(); + + const onEdit = React.useCallback(() => { + if (!fn) return; + navigate(`/functions/edit/${fn.id}`); + }, [fn, navigate]); + + const onDelete = React.useCallback(() => { + if (!fn) return; + deleteCallableFunction(fn.id); + }, [fn]); + + const onExport = React.useCallback(() => { + if (!fn) return; + const data = JSON.stringify( + callableFunctionExportschema.parse({ + callableFunction: fn.toJSON(), + code: getFunctionCode(fn.id), + displayName: getFunctionDisplayName(fn.id) ?? "[N/A]", + }) + ); + const blob = new Blob([data], { type: "application/json" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `${fn.id}.json`; + a.click(); + URL.revokeObjectURL(url); + }, [fn, getFunctionCode, getFunctionDisplayName]); + + return ( + + + + + + + + }> + Edit + + duplicateCallableFunction(id)} + icon={} + > + Duplicate + + }> + Export + + } color="red"> + Delete + + + + ); +}; + +export default CallableFunctionCardMenu; diff --git a/packages/web/src/components/CallableFunctionImport.tsx b/packages/web/src/components/CallableFunctionImport/CallableFunctionImport.tsx similarity index 93% rename from packages/web/src/components/CallableFunctionImport.tsx rename to packages/web/src/components/CallableFunctionImport/CallableFunctionImport.tsx index 5e31792..236ee4e 100644 --- a/packages/web/src/components/CallableFunctionImport.tsx +++ b/packages/web/src/components/CallableFunctionImport/CallableFunctionImport.tsx @@ -1,5 +1,5 @@ import React, { Suspense } from "react"; -import { CallableFunctionExport } from "../entities/callableFunctionExport"; +import { CallableFunctionExport } from "../../entities/callableFunctionExport"; import CallableFunctionImportDropzone from "./CallableFunctionImportDropzone"; import { Accordion, @@ -13,10 +13,12 @@ import { } from "@mantine/core"; import { v4 as uuid } from "uuid"; import { BiX } from "react-icons/bi"; -import TippedActionIcon from "./TippedActionIcon"; -import getFunctionSignature from "../utils/getFunctionSignature"; +import TippedActionIcon from "../common/TippedActionIcon"; +import getFunctionSignature from "../../utils/getFunctionSignature"; -const CodeEditor = React.lazy(() => import("./CodeEditor")); +const CodeEditor = React.lazy( + () => import("../forms/CallableFunctionForm/CodeEditor") +); interface CallableFunctionImportProps { onImport: (fns: CallableFunctionExport[]) => void; diff --git a/packages/web/src/components/CallableFunctionImportButton.tsx b/packages/web/src/components/CallableFunctionImport/CallableFunctionImportButton.tsx similarity index 56% rename from packages/web/src/components/CallableFunctionImportButton.tsx rename to packages/web/src/components/CallableFunctionImport/CallableFunctionImportButton.tsx index d820747..ed41818 100644 --- a/packages/web/src/components/CallableFunctionImportButton.tsx +++ b/packages/web/src/components/CallableFunctionImport/CallableFunctionImportButton.tsx @@ -2,18 +2,18 @@ import { Button, Modal } from "@mantine/core"; import { BiCheck, BiImport, BiX } from "react-icons/bi"; import { useDisclosure } from "@mantine/hooks"; import CallableFunctionImport from "./CallableFunctionImport"; -import FunctionsImportWarning from "./FunctionsImportWarning"; +import FunctionsImportWarning from "../warnings/FunctionsImportWarning"; import React from "react"; -import { CallableFunctionExport } from "../entities/callableFunctionExport"; -import useCallableFunctions from "../hooks/useCallableFunctions"; +import { CallableFunctionExport } from "../../entities/callableFunctionExport"; import { notifications } from "@mantine/notifications"; -import getErrorInfo from "../utils/getErrorInfo"; -import { useAppStore } from "../store"; -import { addCallableFunction } from "../store/actions/callableFunctions/addCallableFunction"; +import getErrorInfo from "../../utils/getErrorInfo"; +import { addCallableFunction } from "../../store/actions/callableFunctions/addCallableFunction"; +import { useGetUniqueFunctionDisplayName } from "../../store/hooks/callableFunctions/useGetUniqueFunctionDisplayName"; +import { useGetUniqueFunctionName } from "../../store/hooks/callableFunctions/useGetUniqueFunctionName"; const CallableFunctionImportButton = () => { - const callableFunctions = useAppStore((state) => state.callableFunctions); - const { getCallableFunctionDisplayName } = useCallableFunctions(); + const getUniqueFunctionDisplayName = useGetUniqueFunctionDisplayName(); + const getUniqueFunctionName = useGetUniqueFunctionName(); const [ showImportModal, { open: openImportModal, close: closeImportModal }, @@ -21,35 +21,21 @@ const CallableFunctionImportButton = () => { const onImport = React.useCallback( (fns: CallableFunctionExport[]) => { - const usedDisplayNames = callableFunctions.map((fn) => - getCallableFunctionDisplayName(fn.id) - ); - const usedNames = callableFunctions.map((fn) => fn.name); + const createdDisplayNames: string[] = []; + const createdNames: string[] = []; for (const fn of fns) { - const importDisplayName = (() => { - let i = 1; - let copy = fn.displayName; - while ( - usedDisplayNames.some( - (displayName) => displayName === copy - ) - ) { - copy = `${fn.displayName} (${i++})`; - } - return copy; - })(); - usedDisplayNames.push(importDisplayName); + const importDisplayName = getUniqueFunctionDisplayName( + fn.displayName, + createdDisplayNames + ); + createdDisplayNames.push(importDisplayName); - const importName = (() => { - let i = 1; - let copy = fn.callableFunction.name; - while (usedNames.some((name) => name === copy)) { - copy = `${fn.callableFunction.name}${i++}`; - } - return copy; - })(); - usedNames.push(importName); + const importName = getUniqueFunctionName( + fn.callableFunction.name, + createdNames + ); + createdNames.push(importName); try { addCallableFunction( @@ -76,7 +62,7 @@ const CallableFunctionImportButton = () => { closeImportModal(); }, - [callableFunctions, closeImportModal, getCallableFunctionDisplayName] + [closeImportModal, getUniqueFunctionDisplayName, getUniqueFunctionName] ); return ( diff --git a/packages/web/src/components/CallableFunctionImportDropzone.tsx b/packages/web/src/components/CallableFunctionImport/CallableFunctionImportDropzone.tsx similarity index 86% rename from packages/web/src/components/CallableFunctionImportDropzone.tsx rename to packages/web/src/components/CallableFunctionImport/CallableFunctionImportDropzone.tsx index d9539d3..5c26147 100644 --- a/packages/web/src/components/CallableFunctionImportDropzone.tsx +++ b/packages/web/src/components/CallableFunctionImport/CallableFunctionImportDropzone.tsx @@ -5,8 +5,8 @@ import React from "react"; import { CallableFunctionExport, callableFunctionExportschema, -} from "../entities/callableFunctionExport"; -import getErrorInfo from "../utils/getErrorInfo"; +} from "../../entities/callableFunctionExport"; +import getErrorInfo from "../../utils/getErrorInfo"; import { BiX, BiUpload } from "react-icons/bi"; import { BsFiletypeJson } from "react-icons/bs"; @@ -15,11 +15,14 @@ const readImportedFile = (file: File) => { const reader = new FileReader(); reader.onload = () => { try { - resolve( - callableFunctionExportschema.parse( - JSON.parse(reader.result as string) - ) + const result = callableFunctionExportschema.safeParse( + JSON.parse(reader.result as string) ); + if (!result.success) { + reject(result.error); + } else { + resolve(result.data); + } } catch (e) { reject(e); } @@ -43,9 +46,9 @@ const CallableFunctionImportDropzone = ({ const handleDrop = React.useCallback( async (files: File[]) => { const importedFns = await Promise.all( - files.map((file) => { + files.map(async (file) => { try { - return readImportedFile(file); + return await readImportedFile(file); } catch (e) { notifications.show({ title: "Failed to import function", diff --git a/packages/web/src/components/Changelog.tsx b/packages/web/src/components/Changelog.tsx deleted file mode 100644 index 494aded..0000000 --- a/packages/web/src/components/Changelog.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Box, List, Stack, Text, Timeline, Title } from "@mantine/core"; -import { changelog } from "../changelog"; - -const dateStr = (date: Date) => { - return date.toLocaleDateString("en-US", { - year: "numeric", - month: "long", - day: "numeric", - }); -}; - -const Changelog = () => { - return ( - - - {changelog.map(({ version, date, sections }, i) => ( - - - {dateStr(date)} - - - {sections.map(({ label, items }, j) => ( - - {label} - - {items.map((item, k) => ( - - {item} - - ))} - - - ))} - - - ))} - - - ); -}; - -export default Changelog; diff --git a/packages/web/src/components/Changelog/Changelog.tsx b/packages/web/src/components/Changelog/Changelog.tsx new file mode 100644 index 0000000..e1677be --- /dev/null +++ b/packages/web/src/components/Changelog/Changelog.tsx @@ -0,0 +1,90 @@ +import { + Box, + Button, + Center, + List, + Stack, + Text, + Timeline, + Title, +} from "@mantine/core"; +import { changelog } from "../../changelog"; +import { BsGithub } from "react-icons/bs"; + +const dateStr = (date: Date) => { + return date.toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }); +}; + +const Changelog = () => { + return ( + + + {changelog.map( + ({ version, date, sections, description }, i) => ( + + + {dateStr(date)} + + {description && ( + + {description} + + )} + + {sections.map( + ({ label, items, description }, j) => ( + + {label} + {description && ( + + {description} + + )} + + {items.map((item, k) => ( + + {item} + + ))} + + + ) + )} + + + ) + )} + + Changes before this point were not recorded. Please refer to + the repository's commit history if you really need to know! +
+ +
+
+
+
+ ); +}; + +export default Changelog; diff --git a/packages/web/src/components/ChangelogButton.tsx b/packages/web/src/components/Changelog/ChangelogButton.tsx similarity index 87% rename from packages/web/src/components/ChangelogButton.tsx rename to packages/web/src/components/Changelog/ChangelogButton.tsx index 7359f13..33c3993 100644 --- a/packages/web/src/components/ChangelogButton.tsx +++ b/packages/web/src/components/Changelog/ChangelogButton.tsx @@ -2,10 +2,10 @@ import { Modal, ScrollArea, Center, Loader, Text } from "@mantine/core"; import { useDisclosure } from "@mantine/hooks"; import React, { Suspense } from "react"; import { BiCodeAlt } from "react-icons/bi"; -import TippedActionIcon from "./TippedActionIcon"; -import { useAppStore } from "../store"; -import { changelog } from "../changelog"; -import { setLastChangelog } from "../store/actions/appSettings/setLastChangelog"; +import TippedActionIcon from "../common/TippedActionIcon"; +import { useAppStore } from "../../store"; +import { changelog } from "../../changelog"; +import { setLastChangelog } from "../../store/actions/appSettings/setLastChangelog"; const Changelog = React.lazy(() => import("./Changelog")); diff --git a/packages/web/src/components/ConversationNavbar.tsx b/packages/web/src/components/ConversationNavbar.tsx deleted file mode 100644 index d78fcbc..0000000 --- a/packages/web/src/components/ConversationNavbar.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import { - Anchor, - Burger, - Divider, - Group, - MediaQuery, - Modal, - Navbar, - Stack, - Text, - createStyles, - useMantineTheme, -} from "@mantine/core"; -import useConversationManager from "../hooks/useConversationManager"; -import { BiCog, BiPlus } from "react-icons/bi"; -import TippedActionIcon from "./TippedActionIcon"; -import Usage from "./Usage"; -import { useDisclosure, useMediaQuery } from "@mantine/hooks"; -import { BsDiscord, BsGithub } from "react-icons/bs"; -import NavbarConversations from "./NavbarConversations"; -import { AiOutlineFunction } from "react-icons/ai"; -import { Link } from "react-router-dom"; -import SettingsFormModal from "./SettingsFormModal"; -import { FaRegQuestionCircle } from "react-icons/fa"; -import About from "./About"; -import { DISCORD_SERVER_INVITE } from "../config/constants"; -import { useAppStore } from "../store"; -import { setActiveConversation } from "../store/actions/conversations/setActiveConversation"; -import ChangelogButton from "./ChangelogButton"; - -const useStyles = createStyles(() => ({ - burger: { - position: "absolute", - top: 0, - right: 0, - zIndex: 101, - }, -})); - -const AppNavbar = () => { - const showUsage = useAppStore((state) => state.showUsage); - const { activeConversation } = useConversationManager(); - const { classes } = useStyles(); - const theme = useMantineTheme(); - const [navbarOpened, { close: closeNavbar, toggle: toggleNavbar }] = - useDisclosure(); - const [settingsOpened, { open: openSettings, close: closeSettings }] = - useDisclosure(); - const [showAbout, { open: openAbout, close: closeAbout }] = useDisclosure(); - - const isSm = useMediaQuery(`(max-width: ${theme.breakpoints.md})`); - - return ( - <> - - - - - - About GPT Turbo Web} - > - - - - ); -}; - -export default AppNavbar; diff --git a/packages/web/src/components/ConversationNavbar/ConversationNavbar.tsx b/packages/web/src/components/ConversationNavbar/ConversationNavbar.tsx new file mode 100644 index 0000000..2d8ae22 --- /dev/null +++ b/packages/web/src/components/ConversationNavbar/ConversationNavbar.tsx @@ -0,0 +1,53 @@ +import { Divider, Navbar, useMantineTheme } from "@mantine/core"; +import Usage from "../Usage/Usage"; +import { useMediaQuery } from "@mantine/hooks"; +import NavbarConversations from "./NavbarConversations/NavbarConversations"; +import { useAppStore } from "../../store"; +import ConversationNavbarHeader from "./ConversationNavbarHeader/ConversationNavbarHeader"; +import { useActiveConversation } from "../../store/hooks/conversations/useActiveConversation"; +import useConversationNavbar from "../../contexts/hooks/useConversationNavbar"; +import ConversationNavbarBurger from "./ConversationNavbarBurger"; +import ConversationNavbarFooter from "./ConversationNavbarFooter/ConversationNavbarFooter"; + +const ConversationNavbar = () => { + const activeConversation = useActiveConversation(); + const { navbarOpened, closeNavbar } = useConversationNavbar(); + const showUsage = useAppStore((state) => state.showUsage); + const theme = useMantineTheme(); + + const isSm = useMediaQuery(`(max-width: ${theme.breakpoints.md})`); + + return ( + <> + + + + ); +}; + +export default ConversationNavbar; diff --git a/packages/web/src/components/ConversationNavbar/ConversationNavbarBurger.tsx b/packages/web/src/components/ConversationNavbar/ConversationNavbarBurger.tsx new file mode 100644 index 0000000..f046857 --- /dev/null +++ b/packages/web/src/components/ConversationNavbar/ConversationNavbarBurger.tsx @@ -0,0 +1,35 @@ +import { MediaQuery, Burger, createStyles } from "@mantine/core"; +import useConversationNavbar from "../../contexts/hooks/useConversationNavbar"; + +const useStyles = createStyles(() => ({ + burger: { + position: "absolute", + top: 0, + right: 0, + zIndex: 101, + }, +})); + +const ConversationNavbarBurger = () => { + const { classes } = useStyles(); + const { navbarOpened, toggleNavbar } = useConversationNavbar(); + return ( + + + + ); +}; + +export default ConversationNavbarBurger; diff --git a/packages/web/src/components/ConversationNavbar/ConversationNavbarFooter/ConversationNavbarFooter.tsx b/packages/web/src/components/ConversationNavbar/ConversationNavbarFooter/ConversationNavbarFooter.tsx new file mode 100644 index 0000000..06b8532 --- /dev/null +++ b/packages/web/src/components/ConversationNavbar/ConversationNavbarFooter/ConversationNavbarFooter.tsx @@ -0,0 +1,42 @@ +import { Stack, Group, Anchor, Text } from "@mantine/core"; +import { BsGithub, BsDiscord } from "react-icons/bs"; +import { DISCORD_SERVER_INVITE } from "../../../config/constants"; +import AboutButton from "../../About/AboutButton"; +import ChangelogButton from "../../Changelog/ChangelogButton"; +import TippedActionIcon from "../../common/TippedActionIcon"; + +const ConversationNavbarFooter = () => { + return ( + + + + + + + + + + + + + GPT Turbo Web v{APP_VERSION} by{" "} + + Tristan Chin + + + + ); +}; + +export default ConversationNavbarFooter; diff --git a/packages/web/src/components/ConversationNavbar/ConversationNavbarHeader/ConversationNavbarHeader.tsx b/packages/web/src/components/ConversationNavbar/ConversationNavbarHeader/ConversationNavbarHeader.tsx new file mode 100644 index 0000000..ded6b33 --- /dev/null +++ b/packages/web/src/components/ConversationNavbar/ConversationNavbarHeader/ConversationNavbarHeader.tsx @@ -0,0 +1,42 @@ +import { Group } from "@mantine/core"; +import { AiOutlineFunction } from "react-icons/ai"; +import { BiPlus } from "react-icons/bi"; +import { Link } from "react-router-dom"; +import { setActiveConversation } from "../../../store/actions/conversations/setActiveConversation"; +import TippedActionIcon from "../../common/TippedActionIcon"; +import SettingsButton from "./SettingsButton"; +import { useActiveConversation } from "../../../store/hooks/conversations/useActiveConversation"; +import useConversationNavbar from "../../../contexts/hooks/useConversationNavbar"; + +const ConversationNavbarHeader = () => { + const activeConversation = useActiveConversation(); + const { closeNavbar } = useConversationNavbar(); + + return ( + + + {activeConversation && ( + { + setActiveConversation(null); + closeNavbar(); + }} + tip="Add conversation" + > + + + )} + + + + + ); +}; + +export default ConversationNavbarHeader; diff --git a/packages/web/src/components/ConversationNavbar/ConversationNavbarHeader/SettingsButton.tsx b/packages/web/src/components/ConversationNavbar/ConversationNavbarHeader/SettingsButton.tsx new file mode 100644 index 0000000..766244d --- /dev/null +++ b/packages/web/src/components/ConversationNavbar/ConversationNavbarHeader/SettingsButton.tsx @@ -0,0 +1,27 @@ +import { useDisclosure } from "@mantine/hooks"; +import { BiCog } from "react-icons/bi"; +import TippedActionIcon from "../../common/TippedActionIcon"; +import SettingsFormModal from "../../modals/SettingsFormModal"; + +const SettingsButton = () => { + const [settingsOpened, { open: openSettings, close: closeSettings }] = + useDisclosure(); + + return ( + <> + + + + + + ); +}; + +export default SettingsButton; diff --git a/packages/web/src/components/NavbarConverationInfoIcon.tsx b/packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConverationInfoIcon.tsx similarity index 100% rename from packages/web/src/components/NavbarConverationInfoIcon.tsx rename to packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConverationInfoIcon.tsx diff --git a/packages/web/src/components/NavbarConversation.tsx b/packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConversation.tsx similarity index 86% rename from packages/web/src/components/NavbarConversation.tsx rename to packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConversation.tsx index 848c0d3..a27d0a0 100644 --- a/packages/web/src/components/NavbarConversation.tsx +++ b/packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConversation.tsx @@ -7,15 +7,17 @@ import { createStyles, } from "@mantine/core"; import { Conversation } from "gpt-turbo"; -import useConversationManager from "../hooks/useConversationManager"; import { BiCheck, BiPencil, BiTrash, BiX } from "react-icons/bi"; import React from "react"; import { useForm } from "@mantine/form"; -import TippedActionIcon from "./TippedActionIcon"; +import TippedActionIcon from "../../common/TippedActionIcon"; import NavbarConversationInfo from "./NavbarConversationInfo"; -import { setConversationName } from "../store/actions/conversations/setConversationName"; -import { removeConversation } from "../store/actions/conversations/removeConversation"; -import { setActiveConversation } from "../store/actions/conversations/setActiveConversation"; +import { setConversationName } from "../../../store/actions/conversations/setConversationName"; +import { removeConversation } from "../../../store/actions/conversations/removeConversation"; +import { setActiveConversation } from "../../../store/actions/conversations/setActiveConversation"; +import { useActiveConversation } from "../../../store/hooks/conversations/useActiveConversation"; +import { useGetConversationName } from "../../../store/hooks/conversations/useGetConversationName"; +import { DEFAULT_CONVERSATION_NAME } from "../../../config/constants"; interface NavbarConversationProps { conversation: Conversation; @@ -68,8 +70,8 @@ const NavbarConversation = ({ conversation, onClick, }: NavbarConversationProps) => { - const { activeConversation, getConversationName } = - useConversationManager(); + const activeConversation = useActiveConversation(); + const getConversationName = useGetConversationName(); const [isDeleting, setIsDeleting] = React.useState(false); const [isEditing, setIsEditing] = React.useState(false); const isActive = conversation.id === activeConversation?.id; @@ -78,12 +80,16 @@ const NavbarConversation = ({ const editFormRef = React.useRef(null); const editForm = useForm({ initialValues: { - name: getConversationName(conversation.id), + name: + getConversationName(conversation.id) ?? + DEFAULT_CONVERSATION_NAME, }, }); const name = React.useMemo(() => { - return getConversationName(conversation.id); + return ( + getConversationName(conversation.id) ?? DEFAULT_CONVERSATION_NAME + ); }, [conversation.id, getConversationName]); const onEdit = editForm.onSubmit((values) => { diff --git a/packages/web/src/components/NavbarConversationInfo.tsx b/packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConversationInfo.tsx similarity index 98% rename from packages/web/src/components/NavbarConversationInfo.tsx rename to packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConversationInfo.tsx index de427c2..673d2fd 100644 --- a/packages/web/src/components/NavbarConversationInfo.tsx +++ b/packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConversationInfo.tsx @@ -17,7 +17,7 @@ import { } from "react-icons/bs"; import { TbCircleDashed } from "react-icons/tb"; import NavbarConverationInfoIcon from "./NavbarConverationInfoIcon"; -import { useAppStore } from "../store"; +import { useAppStore } from "../../../store"; interface NavbarConversationInfoProps { conversation: Conversation; diff --git a/packages/web/src/components/NavbarConversations.tsx b/packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConversations.tsx similarity index 86% rename from packages/web/src/components/NavbarConversations.tsx rename to packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConversations.tsx index e68064e..0337b9e 100644 --- a/packages/web/src/components/NavbarConversations.tsx +++ b/packages/web/src/components/ConversationNavbar/NavbarConversations/NavbarConversations.tsx @@ -7,13 +7,13 @@ import { createStyles, } from "@mantine/core"; import NavbarConversation from "./NavbarConversation"; -import useConversationManager from "../hooks/useConversationManager"; import React from "react"; -import TippedActionIcon from "./TippedActionIcon"; +import TippedActionIcon from "../../common/TippedActionIcon"; import { BiTrash } from "react-icons/bi"; import { useTimeout } from "@mantine/hooks"; -import { useAppStore } from "../store"; -import { removeConversation } from "../store/actions/conversations/removeConversation"; +import { useAppStore } from "../../../store"; +import { removeConversation } from "../../../store/actions/conversations/removeConversation"; +import { useGetConversationLastEdit } from "../../../store/hooks/conversations/useGetConversationLastEdit"; interface NavbarConversationsProps { onConversationSelect?: () => void; @@ -77,7 +77,7 @@ const NavbarConversations = ({ }: NavbarConversationsProps) => { const { classes } = useStyles(); const conversations = useAppStore((state) => state.conversations); - const { getConversationLastEdit } = useConversationManager(); + const getConversationLastEdit = useGetConversationLastEdit(); const [deleteConfirmation, setdeleteConfirmation] = React.useState< string | null >(null); @@ -88,17 +88,20 @@ const NavbarConversations = ({ return conversations .map((c) => ({ conversation: c, - lastEdit: getConversationLastEdit(c.id), + lastEdit: getConversationLastEdit(c.id) ?? Date.now(), })) .sort((a, b) => b.lastEdit - a.lastEdit) - .reduce((acc, { conversation, lastEdit }) => { - const relativeDate = getRelativeDate(lastEdit); - if (!acc[relativeDate]) { - acc[relativeDate] = []; - } - acc[relativeDate].push(conversation); - return acc; - }, {} as Record); + .reduce( + (acc, { conversation, lastEdit }) => { + const relativeDate = getRelativeDate(lastEdit); + if (!acc[relativeDate]) { + acc[relativeDate] = []; + } + acc[relativeDate].push(conversation); + return acc; + }, + {} as Record + ); }, [conversations, getConversationLastEdit]); const makeDeleteGroup = React.useCallback( diff --git a/packages/web/src/components/ConversationPageShell.tsx b/packages/web/src/components/ConversationPageShell.tsx index 371689e..cc34a52 100644 --- a/packages/web/src/components/ConversationPageShell.tsx +++ b/packages/web/src/components/ConversationPageShell.tsx @@ -1,6 +1,7 @@ import { AppShell, useMantineTheme } from "@mantine/core"; -import ConversationNavbar from "./ConversationNavbar"; +import ConversationNavbar from "./ConversationNavbar/ConversationNavbar"; import { useMediaQuery } from "@mantine/hooks"; +import ConversationNavbarProvider from "../contexts/providers/ConversationNavbarProvider"; interface ConversationPageShellProps { children?: React.ReactNode; @@ -11,19 +12,21 @@ const ConversationPageShell = ({ children }: ConversationPageShellProps) => { const isSm = useMediaQuery(`(max-width: ${theme.breakpoints.md})`); return ( - } - sx={(theme) => ({ - backgroundColor: - theme.colorScheme === "dark" - ? theme.colors.dark[8] - : theme.colors.gray[1], - })} - > - {children} - + + } + sx={(theme) => ({ + backgroundColor: + theme.colorScheme === "dark" + ? theme.colors.dark[8] + : theme.colors.gray[1], + })} + > + {children} + + ); }; diff --git a/packages/web/src/components/CodeBlock.tsx b/packages/web/src/components/Messages/CodeBlock.tsx similarity index 97% rename from packages/web/src/components/CodeBlock.tsx rename to packages/web/src/components/Messages/CodeBlock.tsx index 9910295..43ea67f 100644 --- a/packages/web/src/components/CodeBlock.tsx +++ b/packages/web/src/components/Messages/CodeBlock.tsx @@ -9,7 +9,7 @@ import { import { Prism } from "@mantine/prism"; import { BiCheck } from "react-icons/bi"; import { RxClipboard } from "react-icons/rx"; -import { CodeLanguage } from "../utils/types"; +import { CodeLanguage } from "../../utils/types"; const CodeBlock = ({ language, diff --git a/packages/web/src/components/Message.tsx b/packages/web/src/components/Messages/Message.tsx similarity index 95% rename from packages/web/src/components/Message.tsx rename to packages/web/src/components/Messages/Message.tsx index 1af10f0..3ed831a 100644 --- a/packages/web/src/components/Message.tsx +++ b/packages/web/src/components/Messages/Message.tsx @@ -23,16 +23,16 @@ import { } from "react-icons/bi"; import { AiOutlineFunction } from "react-icons/ai"; import React from "react"; -import TippedActionIcon from "./TippedActionIcon"; +import TippedActionIcon from "../common/TippedActionIcon"; import { useForm } from "@mantine/form"; -import useConversationManager from "../hooks/useConversationManager"; import { notifications } from "@mantine/notifications"; import CodeBlock from "./CodeBlock"; import { useDisclosure, useMediaQuery } from "@mantine/hooks"; -import SavePromptModalBody from "./SavePromptModalBody"; -import { CODE_LANGUAGES } from "../config/constants"; -import { CodeLanguage } from "../utils/types"; -import getErrorInfo from "../utils/getErrorInfo"; +import SavePromptModalBody from "../modals/SavePromptModalBody"; +import { CODE_LANGUAGES } from "../../config/constants"; +import { CodeLanguage } from "../../utils/types"; +import getErrorInfo from "../../utils/getErrorInfo"; +import { useActiveConversation } from "../../store/hooks/conversations/useActiveConversation"; interface MessageProps { message: Message; @@ -56,7 +56,7 @@ const useStyles = createStyles((theme) => ({ })); const MessageComponent = ({ message }: MessageProps) => { - const { activeConversation: conversation } = useConversationManager(); + const conversation = useActiveConversation(); const { classes } = useStyles(); const [isEditing, setIsEditing] = React.useState(false); const form = useForm({ diff --git a/packages/web/src/components/Messages.tsx b/packages/web/src/components/Messages/Messages.tsx similarity index 94% rename from packages/web/src/components/Messages.tsx rename to packages/web/src/components/Messages/Messages.tsx index 52cbb0f..f6941b3 100644 --- a/packages/web/src/components/Messages.tsx +++ b/packages/web/src/components/Messages/Messages.tsx @@ -5,12 +5,12 @@ import { createStyles, useMantineTheme, } from "@mantine/core"; -import useConversationManager from "../hooks/useConversationManager"; import Message from "./Message"; import React from "react"; import { useMediaQuery } from "@mantine/hooks"; import { CallableFunction, FunctionCallMessage } from "gpt-turbo"; -import useCallableFunctions from "../hooks/useCallableFunctions"; +import { useActiveConversation } from "../../store/hooks/conversations/useActiveConversation"; +import { useCallFunction } from "../../store/hooks/callableFunctions/useCallFunction"; const useStyles = createStyles(() => ({ scrollArea: { @@ -21,11 +21,11 @@ const useStyles = createStyles(() => ({ })); const Messages = () => { - const { activeConversation: conversation } = useConversationManager(); + const conversation = useActiveConversation(); const [messages, setMessages] = React.useState( conversation?.getMessages() ?? [] ); - const { callFunction } = useCallableFunctions(); + const callFunction = useCallFunction(); const viewport = React.useRef(null); const [isSticky, setIsSticky] = React.useState(true); const { classes } = useStyles(); diff --git a/packages/web/src/components/NavbarConversations/NavbarConversation.tsx b/packages/web/src/components/NavbarConversations/NavbarConversation.tsx new file mode 100644 index 0000000..fd7dbb6 --- /dev/null +++ b/packages/web/src/components/NavbarConversations/NavbarConversation.tsx @@ -0,0 +1,232 @@ +import { + Anchor, + Group, + Stack, + Text, + TextInput, + createStyles, +} from "@mantine/core"; +import { Conversation } from "gpt-turbo"; +import { BiCheck, BiPencil, BiTrash, BiX } from "react-icons/bi"; +import React from "react"; +import { useForm } from "@mantine/form"; +import TippedActionIcon from "../common/TippedActionIcon"; +import { setConversationName } from "../../store/actions/conversations/setConversationName"; +import { removeConversation } from "../../store/actions/conversations/removeConversation"; +import { setActiveConversation } from "../../store/actions/conversations/setActiveConversation"; +import NavbarConversationInfo from "../ConversationNavbar/NavbarConversations/NavbarConversationInfo"; +import { useActiveConversation } from "../../store/hooks/conversations/useActiveConversation"; +import { useGetConversationName } from "../../store/hooks/conversations/useGetConversationName"; +import { DEFAULT_CONVERSATION_NAME } from "../../config/constants"; + +interface NavbarConversationProps { + conversation: Conversation; + onClick?: () => void; +} + +const useStyles = createStyles((theme, { isActive }: { isActive: boolean }) => { + const dark = theme.colorScheme === "dark"; + let backgroundColor: string | undefined; + let hoverBackgroundColor: string | undefined; + let color: string | undefined; + + if (dark && isActive) { + backgroundColor = theme.colors.blue[7]; + hoverBackgroundColor = theme.colors.blue[8]; + color = theme.white; + } else if (dark && !isActive) { + backgroundColor = undefined; + hoverBackgroundColor = theme.colors.dark[6]; + color = theme.white; + } else if (isActive) { + backgroundColor = theme.colors.blue[1]; + hoverBackgroundColor = theme.colors.blue[2]; + color = theme.colors.blue[7]; + } else { + backgroundColor = undefined; + hoverBackgroundColor = theme.colors.gray[1]; + color = theme.colors.gray[8]; + } + + return { + root: { + display: "flex", + alignItems: "center", + textDecoration: "none", + fontWeight: 600, + borderRadius: theme.radius.sm, + backgroundColor, + color, + + "&:hover": { + textDecoration: "none", + backgroundColor: hoverBackgroundColor, + }, + }, + }; +}); + +const NavbarConversation = ({ + conversation, + onClick, +}: NavbarConversationProps) => { + const activeConversation = useActiveConversation(); + const getConversationName = useGetConversationName(); + const [isDeleting, setIsDeleting] = React.useState(false); + const [isEditing, setIsEditing] = React.useState(false); + const isActive = conversation.id === activeConversation?.id; + const { classes } = useStyles({ isActive }); + + const editFormRef = React.useRef(null); + const editForm = useForm({ + initialValues: { + name: + getConversationName(conversation.id) ?? + DEFAULT_CONVERSATION_NAME, + }, + }); + + const name = React.useMemo(() => { + return ( + getConversationName(conversation.id) ?? DEFAULT_CONVERSATION_NAME + ); + }, [conversation.id, getConversationName]); + + const onEdit = editForm.onSubmit((values) => { + if (values.name && values.name !== name) { + setConversationName(conversation.id, values.name); + } + setIsEditing(false); + }); + + const onDelete = React.useCallback(() => { + removeConversation(conversation.id); + setIsDeleting(false); + }, [conversation.id]); + + const onCancel = React.useCallback(() => { + setIsDeleting(false); + setIsEditing(false); + }, []); + + React.useEffect(() => { + if (!isActive) { + setIsDeleting(false); + setIsEditing(false); + } + }, [isActive]); + + const CancelAction = React.useMemo( + () => ( + { + e.stopPropagation(); + onCancel(); + }} + > + + + ), + [onCancel] + ); + + const Actions = React.useMemo(() => { + if (isDeleting) { + return ( + <> + { + e.stopPropagation(); + onDelete(); + }} + > + + + {CancelAction} + + ); + } + + if (isEditing) { + return ( + <> + { + e.stopPropagation(); + editFormRef.current?.requestSubmit(); + }} + > + + + {CancelAction} + + ); + } + + return ( + <> + { + e.stopPropagation(); + setIsEditing(true); + }} + > + + + { + e.stopPropagation(); + setIsDeleting(true); + }} + > + + + + ); + }, [CancelAction, isDeleting, isEditing, onDelete]); + + return ( + { + if (isEditing) return; + setActiveConversation(conversation.id); + onClick?.(); + }} + > + + + {isEditing ? ( +
+ + + ) : ( + + {name} + + )} + {!isEditing && ( + + )} +
+ {isActive && ( + + {Actions} + + )} +
+
+ ); +}; + +export default NavbarConversation; diff --git a/packages/web/src/components/Usage.tsx b/packages/web/src/components/Usage/Usage.tsx similarity index 81% rename from packages/web/src/components/Usage.tsx rename to packages/web/src/components/Usage/Usage.tsx index 535147a..a0db6b8 100644 --- a/packages/web/src/components/Usage.tsx +++ b/packages/web/src/components/Usage/Usage.tsx @@ -1,15 +1,16 @@ import { Stack, Text, Title } from "@mantine/core"; import { Conversation } from "gpt-turbo"; -import useConversationManager from "../hooks/useConversationManager"; import UsageMetric from "./UsageMetric"; -import getPriceString from "../utils/getPriceString"; +import getPriceString from "../../utils/getPriceString"; +import { useGetConversationName } from "../../store/hooks/conversations/useGetConversationName"; +import { DEFAULT_CONVERSATION_NAME } from "../../config/constants"; interface UsageProps { conversation: Conversation; } const Usage = ({ conversation }: UsageProps) => { - const { getConversationName } = useConversationManager(); + const getConversationName = useGetConversationName(); const metrics: { label: React.ReactNode; @@ -45,7 +46,8 @@ const Usage = ({ conversation }: UsageProps) => { Usage - {getConversationName(conversation.id)} + {getConversationName(conversation.id) ?? + DEFAULT_CONVERSATION_NAME} {metrics.map((metric, i) => ( diff --git a/packages/web/src/components/UsageMetric.tsx b/packages/web/src/components/Usage/UsageMetric.tsx similarity index 100% rename from packages/web/src/components/UsageMetric.tsx rename to packages/web/src/components/Usage/UsageMetric.tsx diff --git a/packages/web/src/components/ContentLoader.tsx b/packages/web/src/components/common/ContentLoader.tsx similarity index 100% rename from packages/web/src/components/ContentLoader.tsx rename to packages/web/src/components/common/ContentLoader.tsx diff --git a/packages/web/src/components/InlineConfirmButton.tsx b/packages/web/src/components/common/InlineConfirmButton.tsx similarity index 100% rename from packages/web/src/components/InlineConfirmButton.tsx rename to packages/web/src/components/common/InlineConfirmButton.tsx diff --git a/packages/web/src/components/TippedActionIcon.tsx b/packages/web/src/components/common/TippedActionIcon.tsx similarity index 100% rename from packages/web/src/components/TippedActionIcon.tsx rename to packages/web/src/components/common/TippedActionIcon.tsx diff --git a/packages/web/src/components/AppCatcher.tsx b/packages/web/src/components/error-handling/AppCatcher.tsx similarity index 100% rename from packages/web/src/components/AppCatcher.tsx rename to packages/web/src/components/error-handling/AppCatcher.tsx diff --git a/packages/web/src/components/StorageLoadError.tsx b/packages/web/src/components/error-handling/StorageLoadError.tsx similarity index 96% rename from packages/web/src/components/StorageLoadError.tsx rename to packages/web/src/components/error-handling/StorageLoadError.tsx index 166b196..2374fa3 100644 --- a/packages/web/src/components/StorageLoadError.tsx +++ b/packages/web/src/components/error-handling/StorageLoadError.tsx @@ -8,11 +8,11 @@ import { Textarea, } from "@mantine/core"; import React from "react"; -import getErrorInfo from "../utils/getErrorInfo"; -import { STORAGE_PERSISTENCE_KEY } from "../config/constants"; +import getErrorInfo from "../../utils/getErrorInfo"; +import { STORAGE_PERSISTENCE_KEY } from "../../config/constants"; import { BiBug } from "react-icons/bi"; import { BsFire } from "react-icons/bs"; -import { storeVersion } from "../store/persist/migrations"; +import { storeVersion } from "../../store/persist/migrations"; interface StorageLoadErrorProps { isMigrationError: boolean; diff --git a/packages/web/src/components/CallableFunctionForm.tsx b/packages/web/src/components/forms/CallableFunctionForm/CallableFunctionForm.tsx similarity index 69% rename from packages/web/src/components/CallableFunctionForm.tsx rename to packages/web/src/components/forms/CallableFunctionForm/CallableFunctionForm.tsx index d04a809..7d95103 100644 --- a/packages/web/src/components/CallableFunctionForm.tsx +++ b/packages/web/src/components/forms/CallableFunctionForm/CallableFunctionForm.tsx @@ -1,22 +1,13 @@ -import { - Button, - Center, - Group, - Loader, - Stack, - Text, - TextInput, -} from "@mantine/core"; +import { Button, Center, Group, Loader, Stack, TextInput } from "@mantine/core"; import CallableFunctionFormProvider, { CallableFunctionFormProviderProps, -} from "../contexts/providers/CallableFunctionFormProvider"; -import useCallableFunctionForm from "../hooks/useCallableFunctionForm"; +} from "../../../contexts/providers/CallableFunctionFormProvider"; +import useCallableFunctionForm from "../../../contexts/hooks/useCallableFunctionForm"; import { Link, useNavigate } from "react-router-dom"; import CallableFunctionFormParameters from "./CallableFunctionFormParameters"; -import OptionalTextInput from "./OptionalTextInput"; -import { modals } from "@mantine/modals"; +import OptionalTextInput from "../../inputs/OptionalTextInput"; import React, { Suspense } from "react"; -import { deleteCallableFunction } from "../store/actions/callableFunctions/deleteCallableFunction"; +import { deleteCallableFunction } from "../../../store/actions/callableFunctions/deleteCallableFunction"; const CallableFunctionFormCode = React.lazy( () => import("./CallableFunctionFormCode") @@ -37,26 +28,15 @@ const CallableFunctionFormProvided = ({ const form = useCallableFunctionForm(); const navigate = useNavigate(); - const openDeleteModal = React.useCallback(() => { + const handleDelete = React.useCallback(async () => { const id = form.values.id; if (!id) return; - modals.openConfirmModal({ - title: `Delete ${form.values.displayName}?`, - centered: true, - children: ( - - Are you sure you want to delete {form.values.displayName}? - This cannot be undone. - - ), - labels: { confirm: "Delete function", cancel: "Cancel" }, - confirmProps: { color: "red" }, - onConfirm: () => { - deleteCallableFunction(id); - navigate("/functions"); - }, - }); - }, [form.values.displayName, form.values.id, navigate]); + + const deleted = await deleteCallableFunction(id); + if (!deleted) return; + + navigate("/functions"); + }, [form.values.id, navigate]); return ( @@ -94,7 +74,7 @@ const CallableFunctionFormProvided = ({ diff --git a/packages/web/src/components/CallableFunctionFormCode.tsx b/packages/web/src/components/forms/CallableFunctionForm/CallableFunctionFormCode.tsx similarity index 95% rename from packages/web/src/components/CallableFunctionFormCode.tsx rename to packages/web/src/components/forms/CallableFunctionForm/CallableFunctionFormCode.tsx index 46bb5de..950a024 100644 --- a/packages/web/src/components/CallableFunctionFormCode.tsx +++ b/packages/web/src/components/forms/CallableFunctionForm/CallableFunctionFormCode.tsx @@ -1,9 +1,9 @@ import { Box, Group, Title, Text, Collapse, Stack, Alert } from "@mantine/core"; import { FaQuestion } from "react-icons/fa"; import CodeEditor from "./CodeEditor"; -import TippedActionIcon from "./TippedActionIcon"; +import TippedActionIcon from "../../common/TippedActionIcon"; import { useDisclosure } from "@mantine/hooks"; -import useCallableFunctionForm from "../hooks/useCallableFunctionForm"; +import useCallableFunctionForm from "../../../contexts/hooks/useCallableFunctionForm"; import { BsFillExclamationTriangleFill } from "react-icons/bs"; const CallableFunctionFormCode = () => { diff --git a/packages/web/src/components/CallableFunctionFormParameters.tsx b/packages/web/src/components/forms/CallableFunctionForm/CallableFunctionFormParameters.tsx similarity index 95% rename from packages/web/src/components/CallableFunctionFormParameters.tsx rename to packages/web/src/components/forms/CallableFunctionForm/CallableFunctionFormParameters.tsx index e3ec62d..3ea2d2a 100644 --- a/packages/web/src/components/CallableFunctionFormParameters.tsx +++ b/packages/web/src/components/forms/CallableFunctionForm/CallableFunctionFormParameters.tsx @@ -3,12 +3,12 @@ import { useDisclosure } from "@mantine/hooks"; import { BiPlus, BiX } from "react-icons/bi"; import CallableFunctionParameterForm, { CallableFunctionParameterFormProps, -} from "./CallableFunctionParameterForm"; -import useCallableFunctionForm from "../hooks/useCallableFunctionForm"; +} from "../CallableFunctionParameterForm/CallableFunctionParameterForm"; +import useCallableFunctionForm from "../../../contexts/hooks/useCallableFunctionForm"; import React from "react"; import { JsonSchemaObject } from "gpt-turbo"; -import getFunctionParameters from "../utils/getFunctionParameters"; -import TippedActionIcon from "./TippedActionIcon"; +import getFunctionParameters from "../../../utils/getFunctionParameters"; +import TippedActionIcon from "../../common/TippedActionIcon"; import { modals } from "@mantine/modals"; const CallableFunctionFormParameters = () => { diff --git a/packages/web/src/components/CodeEditor.tsx b/packages/web/src/components/forms/CallableFunctionForm/CodeEditor.tsx similarity index 98% rename from packages/web/src/components/CodeEditor.tsx rename to packages/web/src/components/forms/CallableFunctionForm/CodeEditor.tsx index 2388969..44f92d5 100644 --- a/packages/web/src/components/CodeEditor.tsx +++ b/packages/web/src/components/forms/CallableFunctionForm/CodeEditor.tsx @@ -7,7 +7,7 @@ import jsLanguageSyntax from "highlight.js/lib/languages/javascript"; import React from "react"; import { Divider, createStyles } from "@mantine/core"; import { JsonSchemaObject } from "gpt-turbo"; -import getFunctionSignature from "../utils/getFunctionSignature"; +import getFunctionSignature from "../../../utils/getFunctionSignature"; lowlight.registerLanguage("js", jsLanguageSyntax); diff --git a/packages/web/src/components/CallableFunctionParameterForm.tsx b/packages/web/src/components/forms/CallableFunctionParameterForm/CallableFunctionParameterForm.tsx similarity index 98% rename from packages/web/src/components/CallableFunctionParameterForm.tsx rename to packages/web/src/components/forms/CallableFunctionParameterForm/CallableFunctionParameterForm.tsx index 4c35821..e8637bd 100644 --- a/packages/web/src/components/CallableFunctionParameterForm.tsx +++ b/packages/web/src/components/forms/CallableFunctionParameterForm/CallableFunctionParameterForm.tsx @@ -26,10 +26,10 @@ import { } from "gpt-turbo"; import { z } from "zod"; import { useForm, zodResolver } from "@mantine/form"; -import OptionalTextInput from "./OptionalTextInput"; -import OptionalBooleanInput from "./OptionalBooleanInput"; +import OptionalTextInput from "../../inputs/OptionalTextInput"; +import OptionalBooleanInput from "../../inputs/OptionalBooleanInput"; import { BiInfoCircle } from "react-icons/bi"; -import getErrorInfo from "../utils/getErrorInfo"; +import getErrorInfo from "../../../utils/getErrorInfo"; type CallableFunctionParameterType = | "string" diff --git a/packages/web/src/components/ConversationForm.tsx b/packages/web/src/components/forms/ConversationForm/ConversationForm.tsx similarity index 94% rename from packages/web/src/components/ConversationForm.tsx rename to packages/web/src/components/forms/ConversationForm/ConversationForm.tsx index f14e82d..b01153a 100644 --- a/packages/web/src/components/ConversationForm.tsx +++ b/packages/web/src/components/forms/ConversationForm/ConversationForm.tsx @@ -1,13 +1,13 @@ import { Stack, Text, Button, Tabs, Box } from "@mantine/core"; import ConversationFormProvider, { ConversationFormProviderProps, -} from "../contexts/providers/ConversationFormProvider"; +} from "../../../contexts/providers/ConversationFormProvider"; import ConversationFormConversationTab from "./ConversationFormConversationTab"; import ConversationFormRequestTab from "./ConversationFormRequestTab"; import ConversationFormFunctionsTab from "./ConversationFormFunctionsTab"; -import useConversationForm from "../hooks/useConversationForm"; -import AppSettings from "./AppSettings"; +import AppSettings from "../../AppSettings/AppSettings"; import React from "react"; +import useConversationForm from "../../../contexts/hooks/useConversationForm"; interface ConversationFormProvidedProps { hideAppSettings?: boolean; diff --git a/packages/web/src/components/ConversationFormConversationTab.tsx b/packages/web/src/components/forms/ConversationForm/ConversationFormConversationTab.tsx similarity index 85% rename from packages/web/src/components/ConversationFormConversationTab.tsx rename to packages/web/src/components/forms/ConversationForm/ConversationFormConversationTab.tsx index 798f053..6f8fb1d 100644 --- a/packages/web/src/components/ConversationFormConversationTab.tsx +++ b/packages/web/src/components/forms/ConversationForm/ConversationFormConversationTab.tsx @@ -7,18 +7,18 @@ import { Grid, TextInput, } from "@mantine/core"; -import useConversationForm from "../hooks/useConversationForm"; -import ApiKeyInput from "./ApiKeyInput"; -import ContextInput from "./ContextInput"; -import DisableModerationInput from "./DisableModerationInput"; -import DryInput from "./DryInput"; -import LogitBiasInput from "./LogitBiasInput"; -import ModelSelectInput from "./ModelSelectInput"; -import OptionalNumberInput from "./OptionalNumberInput"; -import SaveInput from "./SaveInput"; -import StopInput from "./StopInput"; -import StreamInput from "./StreamInput"; +import ApiKeyInput from "../../inputs/ApiKeyInput"; +import ContextInput from "../../inputs/ContextInput"; +import DisableModerationInput from "../../inputs/DisableModerationInput"; +import DryInput from "../../inputs/DryInput"; +import LogitBiasInput from "../../inputs/LogitBiasInput"; +import ModelSelectInput from "../../inputs/ModelSelectInput/ModelSelectInput"; +import OptionalNumberInput from "../../inputs/OptionalNumberInput"; +import SaveInput from "../../inputs/SaveInput"; +import StopInput from "../../inputs/StopInput"; +import StreamInput from "../../inputs/StreamInput"; import React from "react"; +import useConversationForm from "../../../contexts/hooks/useConversationForm"; const ConversationFormConversationTab = () => { const form = useConversationForm(); diff --git a/packages/web/src/components/ConversationFormFunctionsTab.tsx b/packages/web/src/components/forms/ConversationForm/ConversationFormFunctionsTab.tsx similarity index 77% rename from packages/web/src/components/ConversationFormFunctionsTab.tsx rename to packages/web/src/components/forms/ConversationForm/ConversationFormFunctionsTab.tsx index a1dcab3..efe4776 100644 --- a/packages/web/src/components/ConversationFormFunctionsTab.tsx +++ b/packages/web/src/components/forms/ConversationForm/ConversationFormFunctionsTab.tsx @@ -1,14 +1,14 @@ import { Button, Center, Chip, Divider, Group, Stack } from "@mantine/core"; -import FunctionsWarning from "./FunctionsWarning"; -import useConversationForm from "../hooks/useConversationForm"; +import FunctionsWarning from "../../warnings/FunctionsWarning"; import { AiOutlineFunction } from "react-icons/ai"; import { Link } from "react-router-dom"; -import useCallableFunctions from "../hooks/useCallableFunctions"; -import { useAppStore } from "../store"; +import { useAppStore } from "../../../store"; +import { useGetFunctionDisplayName } from "../../../store/hooks/callableFunctions/useGetFunctionDisplayName"; +import useConversationForm from "../../../contexts/hooks/useConversationForm"; const ConversationFormFunctionsTab = () => { const callableFunctions = useAppStore((state) => state.callableFunctions); - const { getCallableFunctionDisplayName } = useCallableFunctions(); + const getFunctionDisplayName = useGetFunctionDisplayName(); const form = useConversationForm(); return ( @@ -37,7 +37,8 @@ const ConversationFormFunctionsTab = () => { {callableFunctions.map((fn) => ( - {getCallableFunctionDisplayName(fn.id)} + {getFunctionDisplayName(fn.id) ?? + "[N/A]"} ))} diff --git a/packages/web/src/components/ConversationFormRequestTab.tsx b/packages/web/src/components/forms/ConversationForm/ConversationFormRequestTab.tsx similarity index 65% rename from packages/web/src/components/ConversationFormRequestTab.tsx rename to packages/web/src/components/forms/ConversationForm/ConversationFormRequestTab.tsx index 9f3e666..6cadffb 100644 --- a/packages/web/src/components/ConversationFormRequestTab.tsx +++ b/packages/web/src/components/forms/ConversationForm/ConversationFormRequestTab.tsx @@ -1,7 +1,7 @@ import { Stack } from "@mantine/core"; -import useConversationForm from "../hooks/useConversationForm"; -import HeadersInput from "./HeadersInput"; -import ProxyInput from "./ProxyInput"; +import HeadersInput from "../../inputs/HeadersInput"; +import ProxyInput from "../../inputs/ProxyInput"; +import useConversationForm from "../../../contexts/hooks/useConversationForm"; const ConversationFormRequestTab = () => { const form = useConversationForm(); diff --git a/packages/web/src/components/ApiKeyInput.tsx b/packages/web/src/components/inputs/ApiKeyInput.tsx similarity index 100% rename from packages/web/src/components/ApiKeyInput.tsx rename to packages/web/src/components/inputs/ApiKeyInput.tsx diff --git a/packages/web/src/components/ContextInput.tsx b/packages/web/src/components/inputs/ContextInput.tsx similarity index 91% rename from packages/web/src/components/ContextInput.tsx rename to packages/web/src/components/inputs/ContextInput.tsx index f11d901..aa40a5a 100644 --- a/packages/web/src/components/ContextInput.tsx +++ b/packages/web/src/components/inputs/ContextInput.tsx @@ -1,10 +1,10 @@ import { Modal, ScrollArea, Stack, Textarea } from "@mantine/core"; import { BiFolder, BiSave } from "react-icons/bi"; -import TippedActionIcon from "./TippedActionIcon"; +import TippedActionIcon from "../common/TippedActionIcon"; import { useDisclosure } from "@mantine/hooks"; -import SaveContextModalBody from "./SavePromptModalBody"; -import SavedContextsModalBody from "./SavedPromptsModalBody"; -import { useAppStore } from "../store"; +import SaveContextModalBody from "../modals/SavePromptModalBody"; +import SavedContextsModalBody from "../modals/SavedPromptsModalBody"; +import { useAppStore } from "../../store"; interface ContextInputProps { value: string; diff --git a/packages/web/src/components/DisableModerationInput.tsx b/packages/web/src/components/inputs/DisableModerationInput.tsx similarity index 100% rename from packages/web/src/components/DisableModerationInput.tsx rename to packages/web/src/components/inputs/DisableModerationInput.tsx diff --git a/packages/web/src/components/DryInput.tsx b/packages/web/src/components/inputs/DryInput.tsx similarity index 100% rename from packages/web/src/components/DryInput.tsx rename to packages/web/src/components/inputs/DryInput.tsx diff --git a/packages/web/src/components/HeadersInput.tsx b/packages/web/src/components/inputs/HeadersInput.tsx similarity index 95% rename from packages/web/src/components/HeadersInput.tsx rename to packages/web/src/components/inputs/HeadersInput.tsx index 63c3dda..11e8d78 100644 --- a/packages/web/src/components/HeadersInput.tsx +++ b/packages/web/src/components/inputs/HeadersInput.tsx @@ -41,10 +41,13 @@ const getValueFromHeaders = (value: Header[]) => { if (headers.length === 0) return undefined; - return headers.reduce((acc, { header, value: v }) => { - acc[header] = v; - return acc; - }, {} as Record); + return headers.reduce( + (acc, { header, value: v }) => { + acc[header] = v; + return acc; + }, + {} as Record + ); }; const HeadersInput = ({ value, onChange }: HeadersInputProps) => { diff --git a/packages/web/src/components/LogitBiasInput.tsx b/packages/web/src/components/inputs/LogitBiasInput.tsx similarity index 96% rename from packages/web/src/components/LogitBiasInput.tsx rename to packages/web/src/components/inputs/LogitBiasInput.tsx index eafce2d..e6013e0 100644 --- a/packages/web/src/components/LogitBiasInput.tsx +++ b/packages/web/src/components/inputs/LogitBiasInput.tsx @@ -51,10 +51,13 @@ const getValueFromTokens = (value: LogitBias[]) => { if (tokens.length === 0) return undefined; - return tokens.reduce((acc, { token, probability }) => { - acc[token] = Number(probability); - return acc; - }, {} as Record); + return tokens.reduce( + (acc, { token, probability }) => { + acc[token] = Number(probability); + return acc; + }, + {} as Record + ); }; const LogitBiasInput = ({ value, onChange }: LogitBiasInputProps) => { diff --git a/packages/web/src/components/ModelSelectInput.tsx b/packages/web/src/components/inputs/ModelSelectInput/ModelSelectInput.tsx similarity index 100% rename from packages/web/src/components/ModelSelectInput.tsx rename to packages/web/src/components/inputs/ModelSelectInput/ModelSelectInput.tsx diff --git a/packages/web/src/components/ModelSelectItem.tsx b/packages/web/src/components/inputs/ModelSelectInput/ModelSelectItem.tsx similarity index 100% rename from packages/web/src/components/ModelSelectItem.tsx rename to packages/web/src/components/inputs/ModelSelectInput/ModelSelectItem.tsx diff --git a/packages/web/src/components/OptionalBooleanInput.tsx b/packages/web/src/components/inputs/OptionalBooleanInput.tsx similarity index 100% rename from packages/web/src/components/OptionalBooleanInput.tsx rename to packages/web/src/components/inputs/OptionalBooleanInput.tsx diff --git a/packages/web/src/components/OptionalNumberInput.tsx b/packages/web/src/components/inputs/OptionalNumberInput.tsx similarity index 100% rename from packages/web/src/components/OptionalNumberInput.tsx rename to packages/web/src/components/inputs/OptionalNumberInput.tsx diff --git a/packages/web/src/components/OptionalTextInput.tsx b/packages/web/src/components/inputs/OptionalTextInput.tsx similarity index 100% rename from packages/web/src/components/OptionalTextInput.tsx rename to packages/web/src/components/inputs/OptionalTextInput.tsx diff --git a/packages/web/src/components/Prompt.tsx b/packages/web/src/components/inputs/Prompt/Prompt.tsx similarity index 92% rename from packages/web/src/components/Prompt.tsx rename to packages/web/src/components/inputs/Prompt/Prompt.tsx index b53de0d..35c7add 100644 --- a/packages/web/src/components/Prompt.tsx +++ b/packages/web/src/components/inputs/Prompt/Prompt.tsx @@ -7,7 +7,6 @@ import { Textarea, } from "@mantine/core"; import { useForm } from "@mantine/form"; -import useConversationManager from "../hooks/useConversationManager"; import { notifications } from "@mantine/notifications"; import React from "react"; import PromptUsage from "./PromptUsage"; @@ -20,15 +19,16 @@ import { useOs, } from "@mantine/hooks"; import { BiFolder, BiPaperPlane } from "react-icons/bi"; -import TippedActionIcon from "./TippedActionIcon"; -import SavedPromptsModalBody from "./SavedPromptsModalBody"; -import getErrorInfo from "../utils/getErrorInfo"; -import { useAppStore } from "../store"; -import { setConversationLastEdit } from "../store/actions/conversations/setConversationLastEdit"; +import TippedActionIcon from "../../common/TippedActionIcon"; +import SavedPromptsModalBody from "../../modals/SavedPromptsModalBody"; +import getErrorInfo from "../../../utils/getErrorInfo"; +import { useAppStore } from "../../../store"; +import { setConversationLastEdit } from "../../../store/actions/conversations/setConversationLastEdit"; +import { useActiveConversation } from "../../../store/hooks/conversations/useActiveConversation"; const Prompt = () => { const showUsage = useAppStore((state) => state.showUsage); - const { activeConversation: conversation } = useConversationManager(); + const conversation = useActiveConversation(); const form = useForm({ initialValues: { prompt: "", diff --git a/packages/web/src/components/PromptUsage.tsx b/packages/web/src/components/inputs/Prompt/PromptUsage.tsx similarity index 96% rename from packages/web/src/components/PromptUsage.tsx rename to packages/web/src/components/inputs/Prompt/PromptUsage.tsx index 4d0a09a..e9142ee 100644 --- a/packages/web/src/components/PromptUsage.tsx +++ b/packages/web/src/components/inputs/Prompt/PromptUsage.tsx @@ -1,6 +1,6 @@ import { Tooltip, Stack, Group, Button, Text } from "@mantine/core"; import { getMessageSize, getMessageCost, Conversation } from "gpt-turbo"; -import getPriceString from "../utils/getPriceString"; +import getPriceString from "../../../utils/getPriceString"; import React from "react"; interface PromptUsageProps { diff --git a/packages/web/src/components/ProxyInput.tsx b/packages/web/src/components/inputs/ProxyInput.tsx similarity index 100% rename from packages/web/src/components/ProxyInput.tsx rename to packages/web/src/components/inputs/ProxyInput.tsx diff --git a/packages/web/src/components/SaveInput.tsx b/packages/web/src/components/inputs/SaveInput.tsx similarity index 100% rename from packages/web/src/components/SaveInput.tsx rename to packages/web/src/components/inputs/SaveInput.tsx diff --git a/packages/web/src/components/StopInput.tsx b/packages/web/src/components/inputs/StopInput.tsx similarity index 100% rename from packages/web/src/components/StopInput.tsx rename to packages/web/src/components/inputs/StopInput.tsx diff --git a/packages/web/src/components/StreamInput.tsx b/packages/web/src/components/inputs/StreamInput.tsx similarity index 100% rename from packages/web/src/components/StreamInput.tsx rename to packages/web/src/components/inputs/StreamInput.tsx diff --git a/packages/web/src/components/SavePromptModalBody.tsx b/packages/web/src/components/modals/SavePromptModalBody.tsx similarity index 86% rename from packages/web/src/components/SavePromptModalBody.tsx rename to packages/web/src/components/modals/SavePromptModalBody.tsx index 7072714..4361c7e 100644 --- a/packages/web/src/components/SavePromptModalBody.tsx +++ b/packages/web/src/components/modals/SavePromptModalBody.tsx @@ -1,11 +1,11 @@ import { Button, Group, Stack, TextInput } from "@mantine/core"; import { useForm, zodResolver } from "@mantine/form"; -import { persistenceSavedContextSchema } from "../entities/persistenceSavedContext"; +import { persistenceSavedContextSchema } from "../../entities/persistenceSavedContext"; import { z } from "zod"; -import { persistenceSavedPromptSchema } from "../entities/persistenceSavedPrompt"; -import { useAppStore } from "../store"; -import { saveContext } from "../store/actions/savedContexts/saveContext"; -import { savePrompt } from "../store/actions/savedPrompts/savePrompt"; +import { persistenceSavedPromptSchema } from "../../entities/persistenceSavedPrompt"; +import { useAppStore } from "../../store"; +import { saveContext } from "../../store/actions/savedContexts/saveContext"; +import { savePrompt } from "../../store/actions/savedPrompts/savePrompt"; interface SaveContextModalBodyProps { value: string; diff --git a/packages/web/src/components/SavedPromptsModalBody.tsx b/packages/web/src/components/modals/SavedPromptsModalBody.tsx similarity index 89% rename from packages/web/src/components/SavedPromptsModalBody.tsx rename to packages/web/src/components/modals/SavedPromptsModalBody.tsx index 4803475..cdde898 100644 --- a/packages/web/src/components/SavedPromptsModalBody.tsx +++ b/packages/web/src/components/modals/SavedPromptsModalBody.tsx @@ -1,10 +1,10 @@ import { Accordion, Box, Stack } from "@mantine/core"; -import TippedActionIcon from "./TippedActionIcon"; +import TippedActionIcon from "../common/TippedActionIcon"; import { BiImport, BiTrash } from "react-icons/bi"; import React from "react"; -import { useAppStore } from "../store"; -import { removeSavedContext } from "../store/actions/savedContexts/removeSavedContext"; -import { removeSavedPrompt } from "../store/actions/savedPrompts/removeSavedPrompt"; +import { useAppStore } from "../../store"; +import { removeSavedContext } from "../../store/actions/savedContexts/removeSavedContext"; +import { removeSavedPrompt } from "../../store/actions/savedPrompts/removeSavedPrompt"; interface SavedContextsModalBodyProps { onSelect: (value: string) => void; diff --git a/packages/web/src/components/SettingsFormModal.tsx b/packages/web/src/components/modals/SettingsFormModal.tsx similarity index 78% rename from packages/web/src/components/SettingsFormModal.tsx rename to packages/web/src/components/modals/SettingsFormModal.tsx index 621a08d..07195c0 100644 --- a/packages/web/src/components/SettingsFormModal.tsx +++ b/packages/web/src/components/modals/SettingsFormModal.tsx @@ -1,10 +1,10 @@ import { Container, Modal, ModalProps, useMantineTheme } from "@mantine/core"; import { useMediaQuery } from "@mantine/hooks"; import React from "react"; -import { ConversationFormValues } from "../contexts/ConversationFormContext"; -import ConversationForm from "./ConversationForm"; -import { useAppStore } from "../store"; -import { setDefaultSettings } from "../store/actions/defaultConversationSettings/setDefaultSettings"; +import { ConversationFormValues } from "../../contexts/ConversationFormContext"; +import ConversationForm from "../forms/ConversationForm/ConversationForm"; +import { useAppStore } from "../../store"; +import { setDefaultSettings } from "../../store/actions/defaultConversationSettings/setDefaultSettings"; type SettingsFormModalProps = ModalProps; diff --git a/packages/web/src/components/FunctionsImportWarning.tsx b/packages/web/src/components/warnings/FunctionsImportWarning.tsx similarity index 93% rename from packages/web/src/components/FunctionsImportWarning.tsx rename to packages/web/src/components/warnings/FunctionsImportWarning.tsx index 0a2963b..9da16dc 100644 --- a/packages/web/src/components/FunctionsImportWarning.tsx +++ b/packages/web/src/components/warnings/FunctionsImportWarning.tsx @@ -1,7 +1,7 @@ import { Alert, List, Center, Button, Text } from "@mantine/core"; import { FaExclamationTriangle } from "react-icons/fa"; -import { useAppStore } from "../store"; -import { dismissFunctionsImportWarning } from "../store/actions/callableFunctions/dismissFunctionsImportWarning"; +import { useAppStore } from "../../store"; +import { dismissFunctionsImportWarning } from "../../store/actions/callableFunctions/dismissFunctionsImportWarning"; interface FunctionsImportWarningProps { children?: React.ReactNode; diff --git a/packages/web/src/components/FunctionsWarning.tsx b/packages/web/src/components/warnings/FunctionsWarning.tsx similarity index 93% rename from packages/web/src/components/FunctionsWarning.tsx rename to packages/web/src/components/warnings/FunctionsWarning.tsx index d73f432..b28eafb 100644 --- a/packages/web/src/components/FunctionsWarning.tsx +++ b/packages/web/src/components/warnings/FunctionsWarning.tsx @@ -1,7 +1,7 @@ import { Alert, List, Anchor, Center, Button } from "@mantine/core"; import { FaExclamationTriangle } from "react-icons/fa"; -import { useAppStore } from "../store"; -import { dismissFunctionsWarning } from "../store/actions/callableFunctions/dismissFunctionsWarning"; +import { useAppStore } from "../../store"; +import { dismissFunctionsWarning } from "../../store/actions/callableFunctions/dismissFunctionsWarning"; interface FunctionsWarningProps { children?: React.ReactNode; diff --git a/packages/web/src/config/constants.ts b/packages/web/src/config/constants.ts index f86dd27..74bb6db 100644 --- a/packages/web/src/config/constants.ts +++ b/packages/web/src/config/constants.ts @@ -43,3 +43,5 @@ export const CHANGELOG_SECTION = { FIXES: "🐛 Bug fixes", REMOVALS: "👋 Removals", }; + +export const DEFAULT_CONVERSATION_NAME = "New Chat"; diff --git a/packages/web/src/contexts/CallableFunctionsContext.ts b/packages/web/src/contexts/CallableFunctionsContext.ts deleted file mode 100644 index 971c775..0000000 --- a/packages/web/src/contexts/CallableFunctionsContext.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CallableFunction } from "gpt-turbo"; -import React from "react"; -import makeNotImplemented from "../utils/makeNotImplemented"; - -export interface CallableFunctionsContextValue { - getCallableFunction: (id: string) => CallableFunction; - getCallableFunctionDisplayName: (id: string) => string; - getCallableFunctionCode: (id: string) => string | undefined; - callFunction: ( - id: string, - args: Record - ) => Promise; -} - -const notImplemented = makeNotImplemented("CallableFunctionsContext"); -export const CallableFunctionsContext = - React.createContext({ - getCallableFunction: notImplemented, - getCallableFunctionDisplayName: notImplemented, - getCallableFunctionCode: notImplemented, - callFunction: notImplemented, - }); diff --git a/packages/web/src/contexts/ConversationManagerContext.ts b/packages/web/src/contexts/ConversationManagerContext.ts deleted file mode 100644 index 23b3137..0000000 --- a/packages/web/src/contexts/ConversationManagerContext.ts +++ /dev/null @@ -1,17 +0,0 @@ -import React from "react"; -import makeNotImplemented from "../utils/makeNotImplemented"; -import { Conversation } from "gpt-turbo"; - -export interface ConversationManagerContextValue { - activeConversation: Conversation | null; - getConversationName: (id: string) => string; - getConversationLastEdit: (id: string) => number; -} - -const notImplemented = makeNotImplemented("ConversationManagerContext"); -export const ConversationManagerContext = - React.createContext({ - activeConversation: null, - getConversationName: notImplemented, - getConversationLastEdit: notImplemented, - }); diff --git a/packages/web/src/contexts/ConversationNavbarContext.ts b/packages/web/src/contexts/ConversationNavbarContext.ts new file mode 100644 index 0000000..39e77d1 --- /dev/null +++ b/packages/web/src/contexts/ConversationNavbarContext.ts @@ -0,0 +1,18 @@ +import React from "react"; +import makeNotImplemented from "../utils/makeNotImplemented"; + +export interface ConversationNavbarContextValue { + navbarOpened: boolean; + openNavbar: () => void; + closeNavbar: () => void; + toggleNavbar: () => void; +} + +const notImplemented = makeNotImplemented("ConversationNavbarContext"); +export const ConversationNavbarContext = + React.createContext({ + navbarOpened: false, + openNavbar: notImplemented, + closeNavbar: notImplemented, + toggleNavbar: notImplemented, + }); diff --git a/packages/web/src/contexts/hooks/useCallableFunctionForm.ts b/packages/web/src/contexts/hooks/useCallableFunctionForm.ts new file mode 100644 index 0000000..9269f5c --- /dev/null +++ b/packages/web/src/contexts/hooks/useCallableFunctionForm.ts @@ -0,0 +1,3 @@ +import { CallableFunctionFormContext } from "../CallableFunctionFormContext"; + +export default CallableFunctionFormContext.useContext; diff --git a/packages/web/src/contexts/hooks/useConversationForm.ts b/packages/web/src/contexts/hooks/useConversationForm.ts new file mode 100644 index 0000000..162641f --- /dev/null +++ b/packages/web/src/contexts/hooks/useConversationForm.ts @@ -0,0 +1,3 @@ +import { ConversationFormContext } from "../ConversationFormContext"; + +export default ConversationFormContext.useContext; diff --git a/packages/web/src/contexts/hooks/useConversationNavbar.ts b/packages/web/src/contexts/hooks/useConversationNavbar.ts new file mode 100644 index 0000000..586aa35 --- /dev/null +++ b/packages/web/src/contexts/hooks/useConversationNavbar.ts @@ -0,0 +1,4 @@ +import React from "react"; +import { ConversationNavbarContext } from "../ConversationNavbarContext"; + +export default () => React.useContext(ConversationNavbarContext); diff --git a/packages/web/src/contexts/providers/CallableFunctionFormProvider.tsx b/packages/web/src/contexts/providers/CallableFunctionFormProvider.tsx index 1d8a244..ac9e9bc 100644 --- a/packages/web/src/contexts/providers/CallableFunctionFormProvider.tsx +++ b/packages/web/src/contexts/providers/CallableFunctionFormProvider.tsx @@ -6,10 +6,11 @@ import { } from "../CallableFunctionFormContext"; import { persistenceCallableFunctionSchema } from "../../entities/persistenceCallableFunction"; import React from "react"; -import useCallableFunctions from "../../hooks/useCallableFunctions"; import { useSearchParams } from "react-router-dom"; import functionTemplates from "../../utils/functionTemplates"; import { useAppStore } from "../../store"; +import { useGetFunctionDisplayName } from "../../store/hooks/callableFunctions/useGetFunctionDisplayName"; +import { useGetFunctionCode } from "../../store/hooks/callableFunctions/useGetFunctionCode"; export interface CallableFunctionFormProviderProps { children: React.ReactNode; @@ -23,8 +24,8 @@ const CallableFunctionFormProvider = ({ id, }: CallableFunctionFormProviderProps) => { const callableFunctions = useAppStore((state) => state.callableFunctions); - const { getCallableFunctionDisplayName, getCallableFunctionCode } = - useCallableFunctions(); + const getFunctionDisplayName = useGetFunctionDisplayName(); + const getFunctionCode = useGetFunctionCode(); const form = CallableFunctionFormContext.useForm({ initialValues: { id: uuid(), @@ -43,7 +44,7 @@ const CallableFunctionFormProvider = ({ const existingDisplayName = callableFunctions.find( (f) => f.id !== values.id && - getCallableFunctionDisplayName(f.id) === values.displayName + getFunctionDisplayName(f.id) === values.displayName ); if (existingName) { @@ -67,16 +68,10 @@ const CallableFunctionFormProvider = ({ if (!callableFunction) return; form.setValues({ ...callableFunction.toJSON(), - displayName: getCallableFunctionDisplayName(callableFunction.id), - code: getCallableFunctionCode(callableFunction.id), + displayName: getFunctionDisplayName(callableFunction.id), + code: getFunctionCode(callableFunction.id), }); - }, [ - callableFunctions, - form, - getCallableFunctionCode, - getCallableFunctionDisplayName, - id, - ]); + }, [callableFunctions, form, getFunctionCode, getFunctionDisplayName, id]); React.useEffect(() => { if (form.isDirty() || !query.has("template")) return; diff --git a/packages/web/src/contexts/providers/CallableFunctionsProvider.tsx b/packages/web/src/contexts/providers/CallableFunctionsProvider.tsx deleted file mode 100644 index fa96ad0..0000000 --- a/packages/web/src/contexts/providers/CallableFunctionsProvider.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import React from "react"; -import { - CallableFunctionsContext, - CallableFunctionsContextValue, -} from "../CallableFunctionsContext"; -import { notifications } from "@mantine/notifications"; -import { randomId } from "@mantine/hooks"; -import { BiCheck, BiX } from "react-icons/bi"; -import getErrorInfo from "../../utils/getErrorInfo"; -import { useAppStore } from "../../store"; - -interface CallableFunctionsProviderProps { - children?: React.ReactNode; -} - -const CallableFunctionsProvider = ({ - children, -}: CallableFunctionsProviderProps) => { - const [functions, displayNames, codes] = useAppStore((state) => [ - state.callableFunctions, - state.callableFunctionDisplayNames, - state.callableFunctionCodes, - ]); - - const getCallableFunction = React.useCallback< - CallableFunctionsContextValue["getCallableFunction"] - >( - (id) => { - const callableFunction = functions.find((fn) => fn.id === id); - if (callableFunction === undefined) { - throw new Error(`No callable function found with id "${id}"`); - } - return callableFunction; - }, - [functions] - ); - - const getCallableFunctionDisplayName = React.useCallback< - CallableFunctionsContextValue["getCallableFunctionDisplayName"] - >( - (id) => { - const displayName = displayNames[id]; - if (displayName === undefined) { - throw new Error( - `No display name found for callable function with id "${id}"` - ); - } - return displayName; - }, - [displayNames] - ); - - const getCallableFunctionCode = React.useCallback< - CallableFunctionsContextValue["getCallableFunctionCode"] - >( - (id) => { - return codes[id]; - }, - [codes] - ); - - const callFunction = React.useCallback< - CallableFunctionsContextValue["callFunction"] - >( - async (id, args) => { - const callableFunction = getCallableFunction(id); - const displayName = getCallableFunctionDisplayName(id); - - const code = getCallableFunctionCode(id); - if (!code) return undefined; - - try { - const { argNames, argValues } = - callableFunction.parameters.reduce( - (acc, fn) => { - acc.argNames.push(fn.name); - acc.argValues.push(args[fn.name] ?? undefined); - return acc; - }, - { - argNames: [], - argValues: [], - } as { argNames: string[]; argValues: any[] } - ); - const fn = new Function(...argNames, code); - - const result = fn(...argValues); - - if (result instanceof Promise) { - const notifId = randomId(); - notifications.show({ - id: notifId, - loading: true, - title: "Function Call", - message: `Calling ${displayName}...`, - autoClose: false, - withCloseButton: false, - }); - - try { - const awaited = await result; - - notifications.update({ - id: notifId, - color: "green", - title: "Function Call Success", - message: `${displayName} called successfully!`, - icon: , - autoClose: 2000, - }); - - return awaited; - } catch (e) { - notifications.hide(notifId); - throw e; - } - } - - return result; - } catch (e) { - console.error(e); - const { message } = getErrorInfo(e); - notifications.show({ - color: "red", - title: "Function Call Failed", - message: `${displayName} failed: ${message}`, - icon: , - autoClose: false, - withCloseButton: true, - }); - return undefined; - } - }, - [ - getCallableFunction, - getCallableFunctionCode, - getCallableFunctionDisplayName, - ] - ); - - const providerValue = React.useMemo( - () => ({ - getCallableFunction, - getCallableFunctionDisplayName, - getCallableFunctionCode, - callFunction, - }), - [ - callFunction, - getCallableFunction, - getCallableFunctionCode, - getCallableFunctionDisplayName, - ] - ); - - return ( - - {children} - - ); -}; - -export default CallableFunctionsProvider; diff --git a/packages/web/src/contexts/providers/ConversationManagerProvider.tsx b/packages/web/src/contexts/providers/ConversationManagerProvider.tsx deleted file mode 100644 index 2dead98..0000000 --- a/packages/web/src/contexts/providers/ConversationManagerProvider.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from "react"; -import { - ConversationManagerContext, - ConversationManagerContextValue, -} from "../ConversationManagerContext"; -import { useAppStore } from "../../store"; -import { persistStore } from "../../store/persist/triggerPersist"; - -interface ConversationManagerProviderProps { - children?: React.ReactNode; -} - -const ConversationManagerProvider = ({ - children, -}: ConversationManagerProviderProps) => { - const [conversations, names, lastEdits, activeId] = useAppStore((state) => [ - state.conversations, - state.conversationNames, - state.conversationLastEdits, - state.activeConversationId, - ]); - - const activeConversation = React.useMemo( - () => conversations.find((c) => c.id === activeId) ?? null, - [activeId, conversations] - ); - - const getConversationName = React.useCallback( - (id: string) => { - return names.get(id) ?? "New Chat"; - }, - [names] - ); - - const getConversationLastEdit = React.useCallback( - (id: string) => { - return lastEdits.get(id) ?? Date.now(); - }, - [lastEdits] - ); - - const providerValue = React.useMemo( - () => ({ - activeId, - activeConversation, - getConversationName, - getConversationLastEdit, - }), - [ - activeConversation, - activeId, - getConversationLastEdit, - getConversationName, - ] - ); - - React.useEffect(() => { - if (!activeConversation) return; - const unsubs: (() => void)[] = []; - - unsubs.push( - activeConversation.onMessageAdded((message) => { - if (message.content) { - persistStore(); - } - - unsubs.push( - message.onMessageStreamingStop(() => { - persistStore(); - }) - ); - }), - activeConversation.onMessageRemoved(() => { - persistStore(); - }) - ); - - return () => { - unsubs.forEach((u) => u()); - }; - }, [activeConversation]); - - return ( - - {children} - - ); -}; - -export default ConversationManagerProvider; diff --git a/packages/web/src/contexts/providers/ConversationNavbarProvider.tsx b/packages/web/src/contexts/providers/ConversationNavbarProvider.tsx new file mode 100644 index 0000000..602496c --- /dev/null +++ b/packages/web/src/contexts/providers/ConversationNavbarProvider.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { + ConversationNavbarContext, + ConversationNavbarContextValue, +} from "../ConversationNavbarContext"; +import { useDisclosure } from "@mantine/hooks"; + +interface ConversationNavbarProviderProps { + children?: React.ReactNode; +} + +const ConversationNavbarProvider = ({ + children, +}: ConversationNavbarProviderProps) => { + const [ + navbarOpened, + { open: openNavbar, close: closeNavbar, toggle: toggleNavbar }, + ] = useDisclosure(); + + const providerValue = React.useMemo( + () => ({ + navbarOpened, + openNavbar, + closeNavbar, + toggleNavbar, + }), + [closeNavbar, navbarOpened, openNavbar, toggleNavbar] + ); + + return ( + + {children} + + ); +}; + +export default ConversationNavbarProvider; diff --git a/packages/web/src/contexts/providers/index.tsx b/packages/web/src/contexts/providers/index.tsx index 6271c65..e71d4f5 100644 --- a/packages/web/src/contexts/providers/index.tsx +++ b/packages/web/src/contexts/providers/index.tsx @@ -1,5 +1,3 @@ -import CallableFunctionsProvider from "./CallableFunctionsProvider"; -import ConversationManagerProvider from "./ConversationManagerProvider"; import MantineProviders from "./MantineProviders"; interface ProviderProps { @@ -7,15 +5,7 @@ interface ProviderProps { } const Provider = ({ children }: ProviderProps) => { - return ( - - - - {children} - - - - ); + return {children}; }; export default Provider; diff --git a/packages/web/src/hooks/useCallableFunctionForm.ts b/packages/web/src/hooks/useCallableFunctionForm.ts deleted file mode 100644 index 97ede42..0000000 --- a/packages/web/src/hooks/useCallableFunctionForm.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { CallableFunctionFormContext } from "../contexts/CallableFunctionFormContext"; - -export default CallableFunctionFormContext.useContext; diff --git a/packages/web/src/hooks/useCallableFunctions.ts b/packages/web/src/hooks/useCallableFunctions.ts deleted file mode 100644 index 761c612..0000000 --- a/packages/web/src/hooks/useCallableFunctions.ts +++ /dev/null @@ -1,4 +0,0 @@ -import React from "react"; -import { CallableFunctionsContext } from "../contexts/CallableFunctionsContext"; - -export default () => React.useContext(CallableFunctionsContext); diff --git a/packages/web/src/hooks/useConversationForm.ts b/packages/web/src/hooks/useConversationForm.ts deleted file mode 100644 index fc49e12..0000000 --- a/packages/web/src/hooks/useConversationForm.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ConversationFormContext } from "../contexts/ConversationFormContext"; - -export default ConversationFormContext.useContext; diff --git a/packages/web/src/hooks/useConversationManager.ts b/packages/web/src/hooks/useConversationManager.ts deleted file mode 100644 index 21a2c27..0000000 --- a/packages/web/src/hooks/useConversationManager.ts +++ /dev/null @@ -1,4 +0,0 @@ -import React from "react"; -import { ConversationManagerContext } from "../contexts/ConversationManagerContext"; - -export default () => React.useContext(ConversationManagerContext); diff --git a/packages/web/src/index.tsx b/packages/web/src/index.tsx index 0e24af5..be246a8 100644 --- a/packages/web/src/index.tsx +++ b/packages/web/src/index.tsx @@ -1,7 +1,7 @@ import React from "react"; import ReactDOM from "react-dom/client"; import Providers from "./contexts/providers"; -import AppCatcher from "./components/AppCatcher"; +import AppCatcher from "./components/error-handling/AppCatcher"; import AppRouter from "./routers/AppRouter"; import { Container, Sx } from "@mantine/core"; diff --git a/packages/web/src/pages/ConversationPage.tsx b/packages/web/src/pages/ConversationPage.tsx index 49145a6..dad16a1 100644 --- a/packages/web/src/pages/ConversationPage.tsx +++ b/packages/web/src/pages/ConversationPage.tsx @@ -1,13 +1,40 @@ import { Divider, Stack } from "@mantine/core"; import AddConversation from "../components/AddConversation"; -import useConversationManager from "../hooks/useConversationManager"; -import Messages from "../components/Messages"; -import Prompt from "../components/Prompt"; +import Messages from "../components/Messages/Messages"; +import Prompt from "../components/inputs/Prompt/Prompt"; import ConversationPageShell from "../components/ConversationPageShell"; import React from "react"; +import { persistStore } from "../store/persist/triggerPersist"; +import { useActiveConversation } from "../store/hooks/conversations/useActiveConversation"; const ConversationPage = () => { - const { activeConversation } = useConversationManager(); + const activeConversation = useActiveConversation(); + + React.useEffect(() => { + if (!activeConversation) return; + const unsubs: (() => void)[] = []; + + unsubs.push( + activeConversation.onMessageAdded((message) => { + if (message.content) { + persistStore(); + } + + unsubs.push( + message.onMessageStreamingStop(() => { + persistStore(); + }) + ); + }), + activeConversation.onMessageRemoved(() => { + persistStore(); + }) + ); + + return () => { + unsubs.forEach((u) => u()); + }; + }, [activeConversation]); return ( diff --git a/packages/web/src/pages/FunctionEditorPage.tsx b/packages/web/src/pages/FunctionEditorPage.tsx index 5c9baca..c98edcc 100644 --- a/packages/web/src/pages/FunctionEditorPage.tsx +++ b/packages/web/src/pages/FunctionEditorPage.tsx @@ -1,8 +1,8 @@ import { Container, Button } from "@mantine/core"; import { BiArrowBack } from "react-icons/bi"; import { Link, useNavigate, useParams } from "react-router-dom"; -import FunctionsWarning from "../components/FunctionsWarning"; -import CallableFunctionForm from "../components/CallableFunctionForm"; +import FunctionsWarning from "../components/warnings/FunctionsWarning"; +import CallableFunctionForm from "../components/forms/CallableFunctionForm/CallableFunctionForm"; import React from "react"; import { CallableFunctionFormProviderProps } from "../contexts/providers/CallableFunctionFormProvider"; import { addCallableFunction } from "../store/actions/callableFunctions/addCallableFunction"; diff --git a/packages/web/src/pages/FunctionsPage.tsx b/packages/web/src/pages/FunctionsPage.tsx index 3587547..343e3f9 100644 --- a/packages/web/src/pages/FunctionsPage.tsx +++ b/packages/web/src/pages/FunctionsPage.tsx @@ -10,18 +10,18 @@ import { } from "@mantine/core"; import { BiArrowBack, BiSearch } from "react-icons/bi"; import { Link } from "react-router-dom"; -import FunctionsWarning from "../components/FunctionsWarning"; +import FunctionsWarning from "../components/warnings/FunctionsWarning"; import { useInputState, useMediaQuery } from "@mantine/hooks"; import React from "react"; -import useCallableFunctions from "../hooks/useCallableFunctions"; -import CallableFunctionCard from "../components/CallableFunctionCard"; -import CallableFunctionImportButton from "../components/CallableFunctionImportButton"; +import CallableFunctionCard from "../components/CallableFunctionCard/CallableFunctionCard"; +import CallableFunctionImportButton from "../components/CallableFunctionImport/CallableFunctionImportButton"; import CallableFunctionCreateButton from "../components/CallableFunctionCreateButton"; import { useAppStore } from "../store"; +import { useGetFunctionDisplayName } from "../store/hooks/callableFunctions/useGetFunctionDisplayName"; const FunctionsPage = () => { const callableFunctions = useAppStore((state) => state.callableFunctions); - const { getCallableFunctionDisplayName } = useCallableFunctions(); + const getFunctionDisplayName = useGetFunctionDisplayName(); const [search, setSearch] = useInputState(""); const theme = useMantineTheme(); const isSm = useMediaQuery(`(max-width: ${theme.breakpoints.md})`); @@ -30,10 +30,10 @@ const FunctionsPage = () => { () => callableFunctions.map((fn) => ({ fn, - displayName: getCallableFunctionDisplayName(fn.id), + displayName: getFunctionDisplayName(fn.id), name: fn.name, })), - [callableFunctions, getCallableFunctionDisplayName] + [callableFunctions, getFunctionDisplayName] ); const filteredFunctions = React.useMemo(() => { @@ -46,7 +46,10 @@ const FunctionsPage = () => { }, [detailedFunctions, search]); const filteredDisplayNames = React.useMemo( - () => filteredFunctions.map(({ displayName }) => displayName), + () => + filteredFunctions + .map(({ displayName }) => displayName) + .filter((displayName): displayName is string => !!displayName), [filteredFunctions] ); diff --git a/packages/web/src/routers/AppRouter.tsx b/packages/web/src/routers/AppRouter.tsx index 0d7407f..9d2992e 100644 --- a/packages/web/src/routers/AppRouter.tsx +++ b/packages/web/src/routers/AppRouter.tsx @@ -4,7 +4,7 @@ import { RouterProvider, createBrowserRouter, } from "react-router-dom"; -import ContentLoader from "../components/ContentLoader"; +import ContentLoader from "../components/common/ContentLoader"; import { Text } from "@mantine/core"; const ConversationPage = React.lazy(() => import("../pages/ConversationPage")); diff --git a/packages/web/src/routers/FunctionsRouter.tsx b/packages/web/src/routers/FunctionsRouter.tsx index 7817545..dc4db25 100644 --- a/packages/web/src/routers/FunctionsRouter.tsx +++ b/packages/web/src/routers/FunctionsRouter.tsx @@ -1,6 +1,6 @@ import React, { Suspense } from "react"; import { Routes, Route, Navigate, useMatches } from "react-router-dom"; -import ContentLoader from "../components/ContentLoader"; +import ContentLoader from "../components/common/ContentLoader"; import { Text } from "@mantine/core"; const FunctionsPage = React.lazy(() => import("../pages/FunctionsPage")); diff --git a/packages/web/src/store/actions/appSettings/setLastChangelog.ts b/packages/web/src/store/actions/appSettings/setLastChangelog.ts index e0b4366..b77a5c6 100644 --- a/packages/web/src/store/actions/appSettings/setLastChangelog.ts +++ b/packages/web/src/store/actions/appSettings/setLastChangelog.ts @@ -1,7 +1,7 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const setLastChangelog = (value: string) => { - useAppStore.setState((state) => { +export const setLastChangelog = createAction(({ set }, value: string) => { + set((state) => { state.lastChangelog = value; }); -}; +}, "setLastChangelog"); diff --git a/packages/web/src/store/actions/appSettings/toggleColorScheme.ts b/packages/web/src/store/actions/appSettings/toggleColorScheme.ts index 9a09969..530e433 100644 --- a/packages/web/src/store/actions/appSettings/toggleColorScheme.ts +++ b/packages/web/src/store/actions/appSettings/toggleColorScheme.ts @@ -1,8 +1,12 @@ import { ColorScheme } from "@mantine/core"; -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const toggleColorScheme = (value?: ColorScheme) => { - useAppStore.setState(({ colorScheme }) => ({ - colorScheme: value || (colorScheme === "dark" ? "light" : "dark"), - })); -}; +export const toggleColorScheme = createAction( + ({ set }, value?: ColorScheme) => { + set((state) => { + state.colorScheme = + value || (state.colorScheme === "dark" ? "light" : "dark"); + }); + }, + "toggleColorScheme" +); diff --git a/packages/web/src/store/actions/appSettings/toggleShowUsage.ts b/packages/web/src/store/actions/appSettings/toggleShowUsage.ts index 21e8c16..fc4eae0 100644 --- a/packages/web/src/store/actions/appSettings/toggleShowUsage.ts +++ b/packages/web/src/store/actions/appSettings/toggleShowUsage.ts @@ -1,7 +1,7 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const toggleShowUsage = (showUsage?: boolean) => { - useAppStore.setState((state) => { +export const toggleShowUsage = createAction(({ set }, showUsage?: boolean) => { + set((state) => { state.showUsage = showUsage ?? !state.showUsage; }); -}; +}, "toggleShowUsage"); diff --git a/packages/web/src/store/actions/callableFunctions/addCallableFunction.ts b/packages/web/src/store/actions/callableFunctions/addCallableFunction.ts index fdd560a..776fcce 100644 --- a/packages/web/src/store/actions/callableFunctions/addCallableFunction.ts +++ b/packages/web/src/store/actions/callableFunctions/addCallableFunction.ts @@ -1,29 +1,34 @@ import { CallableFunction, CallableFunctionModel } from "gpt-turbo"; -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const addCallableFunction = ( - config: CallableFunction | CallableFunctionModel, - displayName: string, - code?: string -) => { - const callableFunction = - config instanceof CallableFunction - ? config - : CallableFunction.fromJSON(config); +export const addCallableFunction = createAction( + ( + { set }, + config: CallableFunction | CallableFunctionModel, + displayName: string, + code?: string + ) => { + const callableFunction = + config instanceof CallableFunction + ? config + : CallableFunction.fromJSON(config); - useAppStore.setState((state) => { - state.callableFunctions = state.callableFunctions - .filter((f) => f.id !== callableFunction.id) - .concat(callableFunction); + set((state) => { + state.callableFunctions = state.callableFunctions + .filter((f) => f.id !== callableFunction.id) + .concat(callableFunction); - state.callableFunctionDisplayNames[callableFunction.id] = displayName; + state.callableFunctionDisplayNames[callableFunction.id] = + displayName; - if (code) { - state.callableFunctionCodes[callableFunction.id] = code; - } else if (state.callableFunctionCodes[callableFunction.id]) { - delete state.callableFunctionCodes[callableFunction.id]; - } - }); + if (code) { + state.callableFunctionCodes[callableFunction.id] = code; + } else if (state.callableFunctionCodes[callableFunction.id]) { + delete state.callableFunctionCodes[callableFunction.id]; + } + }); - return callableFunction; -}; + return callableFunction; + }, + "addCallableFunction" +); diff --git a/packages/web/src/store/actions/callableFunctions/callFunction.tsx b/packages/web/src/store/actions/callableFunctions/callFunction.tsx deleted file mode 100644 index ee7b24c..0000000 --- a/packages/web/src/store/actions/callableFunctions/callFunction.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { notifications } from "@mantine/notifications"; -import { randomId } from "@mantine/hooks"; -import { BiCheck, BiX } from "react-icons/bi"; -import getErrorInfo from "../../../utils/getErrorInfo"; -import { useAppStore } from "../.."; - -export const callFunction = async ( - id: string, - args: Record -) => { - const callableFunction = useAppStore - .getState() - .callableFunctions.find((fn) => fn.id === id); - - if (!callableFunction) { - throw new Error(`Function ${id} not found`); - } - - const displayName = - useAppStore.getState().callableFunctionDisplayNames[id] ?? - "Unknown Function"; - - const code = useAppStore.getState().callableFunctionCodes[id]; - if (!code) return undefined; - - try { - const { argNames, argValues } = callableFunction.parameters.reduce( - (acc, fn) => { - acc.argNames.push(fn.name); - acc.argValues.push(args[fn.name] ?? undefined); - return acc; - }, - { - argNames: [], - argValues: [], - } as { argNames: string[]; argValues: any[] } - ); - const fn = new Function(...argNames, code); - - const result = fn(...argValues); - - if (result instanceof Promise) { - const notifId = randomId(); - notifications.show({ - id: notifId, - loading: true, - title: "Function Call", - message: `Calling ${displayName}...`, - autoClose: false, - withCloseButton: false, - }); - - try { - const awaited = await result; - - notifications.update({ - id: notifId, - color: "green", - title: "Function Call Success", - message: `${displayName} called successfully!`, - icon: , - autoClose: 2000, - }); - - return awaited; - } catch (e) { - notifications.hide(notifId); - throw e; - } - } - - return result; - } catch (e) { - console.error(e); - const { message } = getErrorInfo(e); - notifications.show({ - color: "red", - title: "Function Call Failed", - message: `${displayName} failed: ${message}`, - icon: , - autoClose: false, - withCloseButton: true, - }); - return undefined; - } -}; diff --git a/packages/web/src/store/actions/callableFunctions/deleteCallableFunction.ts b/packages/web/src/store/actions/callableFunctions/deleteCallableFunction.ts deleted file mode 100644 index 049e218..0000000 --- a/packages/web/src/store/actions/callableFunctions/deleteCallableFunction.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { useAppStore } from "../.."; - -export const deleteCallableFunction = (id: string) => { - useAppStore.setState((state) => { - state.callableFunctions = state.callableFunctions.filter( - (f) => f.id !== id - ); - delete state.callableFunctionDisplayNames[id]; - delete state.callableFunctionCodes[id]; - }); -}; diff --git a/packages/web/src/store/actions/callableFunctions/deleteCallableFunction.tsx b/packages/web/src/store/actions/callableFunctions/deleteCallableFunction.tsx new file mode 100644 index 0000000..361b09a --- /dev/null +++ b/packages/web/src/store/actions/callableFunctions/deleteCallableFunction.tsx @@ -0,0 +1,50 @@ +import { modals } from "@mantine/modals"; +import { Text } from "@mantine/core"; +import { createAction } from "../createAction"; + +export const deleteCallableFunction = createAction( + async ({ set, get }, id: string, showConfirm = true) => { + const { callableFunctions, callableFunctionDisplayNames } = get(); + + const fn = callableFunctions.find((fn) => fn.id === id); + if (!fn) return false; + + const displayName = callableFunctionDisplayNames[fn.id] ?? "[N/A]"; + + const onConfirm = () => { + set((state) => { + state.callableFunctions = state.callableFunctions.filter( + (f) => f.id !== id + ); + delete state.callableFunctionDisplayNames[id]; + delete state.callableFunctionCodes[id]; + }); + }; + + if (!showConfirm) { + onConfirm(); + return true; + } + + return new Promise((resolve) => { + modals.openConfirmModal({ + title: `Delete ${displayName}?`, + centered: true, + children: ( + + Are you sure you want to delete {displayName}? This + cannot be undone. + + ), + labels: { confirm: "Delete function", cancel: "Cancel" }, + confirmProps: { color: "red" }, + onConfirm: () => { + onConfirm(); + resolve(true); + }, + onClose: () => resolve(false), + }); + }); + }, + "deleteCallableFunction" +); diff --git a/packages/web/src/store/actions/callableFunctions/dismissFunctionsImportWarning.ts b/packages/web/src/store/actions/callableFunctions/dismissFunctionsImportWarning.ts index 5eb4d96..363e6bc 100644 --- a/packages/web/src/store/actions/callableFunctions/dismissFunctionsImportWarning.ts +++ b/packages/web/src/store/actions/callableFunctions/dismissFunctionsImportWarning.ts @@ -1,5 +1,5 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const dismissFunctionsImportWarning = () => { - useAppStore.setState({ showFunctionsImportWarning: false }); -}; +export const dismissFunctionsImportWarning = createAction(({ set }) => { + set({ showFunctionsImportWarning: false }); +}, "dismissFunctionsImportWarning"); diff --git a/packages/web/src/store/actions/callableFunctions/dismissFunctionsWarning.ts b/packages/web/src/store/actions/callableFunctions/dismissFunctionsWarning.ts index 65ed507..cbc4846 100644 --- a/packages/web/src/store/actions/callableFunctions/dismissFunctionsWarning.ts +++ b/packages/web/src/store/actions/callableFunctions/dismissFunctionsWarning.ts @@ -1,5 +1,5 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const dismissFunctionsWarning = () => { - useAppStore.setState({ showFunctionsWarning: false }); -}; +export const dismissFunctionsWarning = createAction(({ set }) => { + set({ showFunctionsWarning: false }); +}, "dismissFunctionsWarning"); diff --git a/packages/web/src/store/actions/callableFunctions/duplicateCallableFunction.tsx b/packages/web/src/store/actions/callableFunctions/duplicateCallableFunction.tsx new file mode 100644 index 0000000..df5b933 --- /dev/null +++ b/packages/web/src/store/actions/callableFunctions/duplicateCallableFunction.tsx @@ -0,0 +1,68 @@ +import { Text } from "@mantine/core"; +import { modals } from "@mantine/modals"; +import { addCallableFunction } from "./addCallableFunction"; +import getUniqueString from "../../../utils/getUniqueString"; +import { createAction } from "../createAction"; + +export const duplicateCallableFunction = createAction( + ({ get }, id: string, withConfirm = true) => { + const { + callableFunctions, + callableFunctionDisplayNames, + callableFunctionCodes, + } = get(); + + const fn = callableFunctions.find((fn) => fn.id === id); + if (!fn) return; + + const displayName = callableFunctionDisplayNames[fn.id]; + if (!displayName) return; + + const copyDisplayName = getUniqueString( + displayName, + Object.values(callableFunctionDisplayNames), + (displayName, i) => `${displayName} (${i})` + ); + + const copyName = getUniqueString( + fn.name, + callableFunctions.map((fn) => fn.name), + (name, i) => `${name}${i}` + ); + + const onConfirm = () => { + addCallableFunction( + { + ...fn.toJSON(), + id: undefined, + name: copyName, + }, + copyDisplayName, + callableFunctionCodes[fn.id] + ); + }; + + if (!withConfirm) return onConfirm(); + + modals.openConfirmModal({ + title: `Duplicate ${displayName}?`, + centered: true, + children: ( + + This will create a new function with the same config and + code, but the display name and function name will appear as{" "} + + {copyDisplayName} + {" "} + and{" "} + + {copyName} + + + ), + labels: { confirm: "Duplicate function", cancel: "Cancel" }, + onConfirm, + }); + }, + "duplicateCallableFunction" +); diff --git a/packages/web/src/store/actions/callableFunctions/removeAllCallableFunctions.ts b/packages/web/src/store/actions/callableFunctions/removeAllCallableFunctions.ts index 3205487..ed63836 100644 --- a/packages/web/src/store/actions/callableFunctions/removeAllCallableFunctions.ts +++ b/packages/web/src/store/actions/callableFunctions/removeAllCallableFunctions.ts @@ -1,9 +1,9 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const removeAllCallableFunctions = () => { - useAppStore.setState((state) => { +export const removeAllCallableFunctions = createAction(({ set }) => { + set((state) => { state.callableFunctions = []; state.callableFunctionDisplayNames = {}; state.callableFunctionCodes = {}; }); -}; +}, "removeAllCallableFunctions"); diff --git a/packages/web/src/store/actions/callableFunctions/resetCallableFunctionWarnings.ts b/packages/web/src/store/actions/callableFunctions/resetCallableFunctionWarnings.ts index 5420ffe..b38757e 100644 --- a/packages/web/src/store/actions/callableFunctions/resetCallableFunctionWarnings.ts +++ b/packages/web/src/store/actions/callableFunctions/resetCallableFunctionWarnings.ts @@ -1,8 +1,8 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const resetCallableFunctionWarnings = () => { - useAppStore.setState((state) => { +export const resetCallableFunctionWarnings = createAction(({ set }) => { + set((state) => { state.showFunctionsWarning = true; state.showFunctionsImportWarning = true; }); -}; +}, "resetCallableFunctionWarnings"); diff --git a/packages/web/src/store/actions/conversations/addConversation.ts b/packages/web/src/store/actions/conversations/addConversation.ts index 33dd5ee..ff741bf 100644 --- a/packages/web/src/store/actions/conversations/addConversation.ts +++ b/packages/web/src/store/actions/conversations/addConversation.ts @@ -3,21 +3,25 @@ import { ConversationConfigParameters, RequestOptions, } from "gpt-turbo"; -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const addConversation = ( - conversation: Conversation | ConversationConfigParameters, - requestOptions?: RequestOptions -) => { - const newConversation = - conversation instanceof Conversation - ? conversation - : new Conversation(conversation, requestOptions); +export const addConversation = createAction( + ( + { set }, + conversation: Conversation | ConversationConfigParameters, + requestOptions?: RequestOptions + ) => { + const newConversation = + conversation instanceof Conversation + ? conversation + : new Conversation(conversation, requestOptions); - useAppStore.setState((state) => { - state.conversations.push(newConversation); - state.conversationLastEdits.set(newConversation.id, Date.now()); - }); + set((state) => { + state.conversations.push(newConversation); + state.conversationLastEdits.set(newConversation.id, Date.now()); + }); - return newConversation; -}; + return newConversation; + }, + "addConversation" +); diff --git a/packages/web/src/store/actions/conversations/removeAllConversations.ts b/packages/web/src/store/actions/conversations/removeAllConversations.ts index d5344e7..45458d0 100644 --- a/packages/web/src/store/actions/conversations/removeAllConversations.ts +++ b/packages/web/src/store/actions/conversations/removeAllConversations.ts @@ -1,11 +1,11 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; import { setActiveConversation } from "./setActiveConversation"; -export const removeAllConversations = () => { - useAppStore.setState((state) => { +export const removeAllConversations = createAction(({ set }) => { + set((state) => { state.conversations = []; state.conversationLastEdits = new Map(); state.conversationNames = new Map(); setActiveConversation(null); }); -}; +}, "removeAllConversations"); diff --git a/packages/web/src/store/actions/conversations/removeConversation.ts b/packages/web/src/store/actions/conversations/removeConversation.ts index 2a3cb5d..676fc6b 100644 --- a/packages/web/src/store/actions/conversations/removeConversation.ts +++ b/packages/web/src/store/actions/conversations/removeConversation.ts @@ -1,8 +1,8 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; import { setActiveConversation } from "./setActiveConversation"; -export const removeConversation = (id: string) => { - useAppStore.setState((state) => { +export const removeConversation = createAction(({ set }, id: string) => { + set((state) => { state.conversations = state.conversations.filter( (conversation) => conversation.id !== id ); @@ -12,4 +12,4 @@ export const removeConversation = (id: string) => { setActiveConversation(state.activeConversationId); }); -}; +}, "removeConversation"); diff --git a/packages/web/src/store/actions/conversations/setActiveConversation.ts b/packages/web/src/store/actions/conversations/setActiveConversation.ts index cd25732..f91b0ae 100644 --- a/packages/web/src/store/actions/conversations/setActiveConversation.ts +++ b/packages/web/src/store/actions/conversations/setActiveConversation.ts @@ -1,22 +1,25 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const setActiveConversation = (id: string | null, force = false) => { - if (force) { - useAppStore.setState({ activeConversationId: id }); - return; - } - if (!id) { - useAppStore.setState({ activeConversationId: null }); - return; - } +export const setActiveConversation = createAction( + ({ get, set }, id: string | null, force = false) => { + if (force) { + set({ activeConversationId: id }); + return; + } + if (!id) { + set({ activeConversationId: null }); + return; + } - const { conversations } = useAppStore.getState(); - const conversation = conversations.find((c) => c.id === id); + const { conversations } = get(); + const conversation = conversations.find((c) => c.id === id); - if (!conversation) { - useAppStore.setState({ activeConversationId: null }); - return; - } + if (!conversation) { + set({ activeConversationId: null }); + return; + } - useAppStore.setState({ activeConversationId: id }); -}; + set({ activeConversationId: id }); + }, + "setActiveConversation" +); diff --git a/packages/web/src/store/actions/conversations/setConversationLastEdit.ts b/packages/web/src/store/actions/conversations/setConversationLastEdit.ts index ab63929..47be3ab 100644 --- a/packages/web/src/store/actions/conversations/setConversationLastEdit.ts +++ b/packages/web/src/store/actions/conversations/setConversationLastEdit.ts @@ -1,7 +1,10 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const setConversationLastEdit = (id: string, lastEdit = Date.now()) => { - useAppStore.setState((state) => { - state.conversationLastEdits.set(id, lastEdit); - }); -}; +export const setConversationLastEdit = createAction( + ({ set }, id: string, lastEdit = Date.now()) => { + set((state) => { + state.conversationLastEdits.set(id, lastEdit); + }); + }, + "setConversationLastEdit" +); diff --git a/packages/web/src/store/actions/conversations/setConversationName.ts b/packages/web/src/store/actions/conversations/setConversationName.ts index 428e103..0d6f7ab 100644 --- a/packages/web/src/store/actions/conversations/setConversationName.ts +++ b/packages/web/src/store/actions/conversations/setConversationName.ts @@ -1,7 +1,10 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const setConversationName = (id: string, name: string) => { - useAppStore.setState((state) => { - state.conversationNames.set(id, name); - }); -}; +export const setConversationName = createAction( + ({ set }, id: string, name: string) => { + set((state) => { + state.conversationNames.set(id, name); + }); + }, + "setConversationName" +); diff --git a/packages/web/src/store/actions/createAction.ts b/packages/web/src/store/actions/createAction.ts new file mode 100644 index 0000000..af0e2f7 --- /dev/null +++ b/packages/web/src/store/actions/createAction.ts @@ -0,0 +1,49 @@ +import { useAppStore } from ".."; + +export type AppStoreSet = typeof useAppStore.setState; +export type AppStoreGet = typeof useAppStore.getState; +export type AppStoreSubscribe = typeof useAppStore.subscribe; +export type AppStorePersist = typeof useAppStore.persist; + +export interface AppStoreMethods { + set: AppStoreSet; + get: AppStoreGet; + subscribe: AppStoreSubscribe; + persist: AppStorePersist; +} + +export type AppStoreAction = ( + storeMethods: AppStoreMethods, + ...args: any[] +) => any; + +export const createAction = < + A extends AppStoreAction, + P extends any[] = A extends ( + storeMethods: AppStoreMethods, + ...args: infer U + ) => any + ? U + : never, + F = (...args: P) => ReturnType, +>( + action: A, + actionName: string | undefined | { type: unknown } = action.name || + undefined +): F => { + const set: AppStoreSet = (nextStateOrUpdater, shouldReplace, action) => { + return useAppStore.setState( + nextStateOrUpdater, + shouldReplace, + action || actionName + ); + }; + + const storeMethods: AppStoreMethods = { + set, + get: useAppStore.getState, + subscribe: useAppStore.subscribe, + persist: useAppStore.persist, + }; + return ((...args: P) => action(storeMethods, ...args)) as F; +}; diff --git a/packages/web/src/store/actions/defaultConversationSettings/resetDefaultSettings.ts b/packages/web/src/store/actions/defaultConversationSettings/resetDefaultSettings.ts index 0bfb775..73862b0 100644 --- a/packages/web/src/store/actions/defaultConversationSettings/resetDefaultSettings.ts +++ b/packages/web/src/store/actions/defaultConversationSettings/resetDefaultSettings.ts @@ -1,9 +1,9 @@ -import { useAppStore } from "../.."; import { initialDefaultConversationSettingsState } from "../../slices/defaultConversationSettingsSlice"; +import { createAction } from "../createAction"; -export const resetDefaultSettings = () => { - useAppStore.setState((state) => { +export const resetDefaultSettings = createAction(({ set }) => { + set((state) => { state.defaultSettings = initialDefaultConversationSettingsState.defaultSettings; }); -}; +}, "resetDefaultSettings"); diff --git a/packages/web/src/store/actions/defaultConversationSettings/setDefaultSettings.ts b/packages/web/src/store/actions/defaultConversationSettings/setDefaultSettings.ts index a96ffb7..ec890a4 100644 --- a/packages/web/src/store/actions/defaultConversationSettings/setDefaultSettings.ts +++ b/packages/web/src/store/actions/defaultConversationSettings/setDefaultSettings.ts @@ -1,8 +1,11 @@ -import { useAppStore } from "../.."; import { PersistenceDefaultSettings } from "../../../entities/persistenceDefaultSettings"; +import { createAction } from "../createAction"; -export const setDefaultSettings = (settings: PersistenceDefaultSettings) => { - useAppStore.setState((state) => { - state.defaultSettings = settings; - }); -}; +export const setDefaultSettings = createAction( + ({ set }, settings: PersistenceDefaultSettings) => { + set((state) => { + state.defaultSettings = settings; + }); + }, + "setDefaultSettings" +); diff --git a/packages/web/src/store/actions/persistence/addPersistedConversationId.ts b/packages/web/src/store/actions/persistence/addPersistedConversationId.ts index 7ee728e..21b346f 100644 --- a/packages/web/src/store/actions/persistence/addPersistedConversationId.ts +++ b/packages/web/src/store/actions/persistence/addPersistedConversationId.ts @@ -1,7 +1,10 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const addPersistedConversationId = (id: string) => { - useAppStore.setState((state) => { - state.persistedConversationIds.push(id); - }); -}; +export const addPersistedConversationId = createAction( + ({ set }, id: string) => { + set((state) => { + state.persistedConversationIds.push(id); + }); + }, + "addPersistedConversationId" +); diff --git a/packages/web/src/store/actions/savedContexts/removeAllSavedContexts.ts b/packages/web/src/store/actions/savedContexts/removeAllSavedContexts.ts index 3403f42..6ec1738 100644 --- a/packages/web/src/store/actions/savedContexts/removeAllSavedContexts.ts +++ b/packages/web/src/store/actions/savedContexts/removeAllSavedContexts.ts @@ -1,7 +1,7 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const removeAllSavedContexts = () => { - useAppStore.setState((state) => { +export const removeAllSavedContexts = createAction(({ set }) => { + set((state) => { state.savedContexts = []; }); -}; +}, "removeAllSavedContexts"); diff --git a/packages/web/src/store/actions/savedContexts/removeSavedContext.ts b/packages/web/src/store/actions/savedContexts/removeSavedContext.ts index 4861e41..9138f11 100644 --- a/packages/web/src/store/actions/savedContexts/removeSavedContext.ts +++ b/packages/web/src/store/actions/savedContexts/removeSavedContext.ts @@ -1,9 +1,12 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const removeSavedContext = (contextName: string) => { - useAppStore.setState((state) => { - state.savedContexts = state.savedContexts.filter( - (context) => context.name !== contextName - ); - }); -}; +export const removeSavedContext = createAction( + ({ set }, contextName: string) => { + set((state) => { + state.savedContexts = state.savedContexts.filter( + (context) => context.name !== contextName + ); + }); + }, + "removeSavedContext" +); diff --git a/packages/web/src/store/actions/savedContexts/saveContext.ts b/packages/web/src/store/actions/savedContexts/saveContext.ts index a737101..f150e6c 100644 --- a/packages/web/src/store/actions/savedContexts/saveContext.ts +++ b/packages/web/src/store/actions/savedContexts/saveContext.ts @@ -1,7 +1,10 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const saveContext = (name: string, value: string) => { - useAppStore.setState((state) => { - state.savedContexts.push({ name, value }); - }); -}; +export const saveContext = createAction( + ({ set }, name: string, value: string) => { + set((state) => { + state.savedContexts.push({ name, value }); + }); + }, + "saveContext" +); diff --git a/packages/web/src/store/actions/savedPrompts/removeAllSavedPrompts.ts b/packages/web/src/store/actions/savedPrompts/removeAllSavedPrompts.ts index f8c052b..8f1360c 100644 --- a/packages/web/src/store/actions/savedPrompts/removeAllSavedPrompts.ts +++ b/packages/web/src/store/actions/savedPrompts/removeAllSavedPrompts.ts @@ -1,7 +1,7 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const removeAllSavedPrompts = () => { - useAppStore.setState((state) => { +export const removeAllSavedPrompts = createAction(({ set }) => { + set((state) => { state.savedPrompts = []; }); -}; +}, "removeAllSavedPrompts"); diff --git a/packages/web/src/store/actions/savedPrompts/removeSavedPrompt.ts b/packages/web/src/store/actions/savedPrompts/removeSavedPrompt.ts index c2dfb9b..d705b53 100644 --- a/packages/web/src/store/actions/savedPrompts/removeSavedPrompt.ts +++ b/packages/web/src/store/actions/savedPrompts/removeSavedPrompt.ts @@ -1,9 +1,9 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const removeSavedPrompt = (promptName: string) => { - useAppStore.setState((state) => { +export const removeSavedPrompt = createAction(({ set }, promptName: string) => { + set((state) => { state.savedPrompts = state.savedPrompts.filter( (prompt) => prompt.name !== promptName ); }); -}; +}, "removeSavedPrompt"); diff --git a/packages/web/src/store/actions/savedPrompts/savePrompt.ts b/packages/web/src/store/actions/savedPrompts/savePrompt.ts index d05b8dd..3889031 100644 --- a/packages/web/src/store/actions/savedPrompts/savePrompt.ts +++ b/packages/web/src/store/actions/savedPrompts/savePrompt.ts @@ -1,7 +1,10 @@ -import { useAppStore } from "../.."; +import { createAction } from "../createAction"; -export const savePrompt = (name: string, value: string) => { - useAppStore.setState((state) => { - state.savedPrompts.push({ name, value }); - }); -}; +export const savePrompt = createAction( + ({ set }, name: string, value: string) => { + set((state) => { + state.savedPrompts.push({ name, value }); + }); + }, + "savePrompt" +); diff --git a/packages/web/src/store/hooks/callableFunctions/useCallFunction.tsx b/packages/web/src/store/hooks/callableFunctions/useCallFunction.tsx new file mode 100644 index 0000000..e631e8b --- /dev/null +++ b/packages/web/src/store/hooks/callableFunctions/useCallFunction.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { useGetFunction } from "./useGetFunction"; +import { useGetFunctionDisplayName } from "./useGetFunctionDisplayName"; +import { useGetFunctionCode } from "./useGetFunctionCode"; +import { randomId } from "@mantine/hooks"; +import { notifications } from "@mantine/notifications"; +import { BiCheck, BiX } from "react-icons/bi"; +import getErrorInfo from "../../../utils/getErrorInfo"; + +export const useCallFunction = () => { + const getFunction = useGetFunction(); + const getDisplayName = useGetFunctionDisplayName(); + const getCode = useGetFunctionCode(); + + return React.useCallback( + async (id: string, args: Record) => { + const callableFunction = getFunction(id); + if (!callableFunction) return undefined; + + const code = getCode(id); + if (!code) return undefined; + + const displayName = getDisplayName(id) ?? "[N/A]"; + + try { + const { argNames, argValues } = + callableFunction.parameters.reduce( + (acc, param) => { + acc.argNames.push(param.name); + acc.argValues.push(args[param.name] ?? undefined); + return acc; + }, + { + argNames: [], + argValues: [], + } as { argNames: string[]; argValues: any[] } + ); + const fn = new Function(...argNames, code); + + const result = fn(...argValues); + + if (result instanceof Promise) { + const notifId = randomId(); + notifications.show({ + id: notifId, + loading: true, + title: "Function Call", + message: `Calling ${displayName}...`, + autoClose: false, + withCloseButton: false, + }); + + try { + const awaited = await result; + + notifications.update({ + id: notifId, + color: "green", + title: "Function Call Success", + message: `${displayName} called successfully!`, + icon: , + autoClose: 2000, + }); + + return awaited; + } catch (e) { + notifications.hide(notifId); + throw e; + } + } + + return result; + } catch (e) { + console.error(e); + const { message } = getErrorInfo(e); + notifications.show({ + color: "red", + title: "Function Call Failed", + message: `${displayName} failed: ${message}`, + icon: , + autoClose: false, + withCloseButton: true, + }); + return undefined; + } + }, + [getCode, getDisplayName, getFunction] + ); +}; diff --git a/packages/web/src/store/hooks/callableFunctions/useGetFunction.ts b/packages/web/src/store/hooks/callableFunctions/useGetFunction.ts new file mode 100644 index 0000000..92977ab --- /dev/null +++ b/packages/web/src/store/hooks/callableFunctions/useGetFunction.ts @@ -0,0 +1,14 @@ +import React from "react"; +import { useAppStore } from "../.."; + +export const useGetFunction = () => { + const functions = useAppStore((state) => state.callableFunctions); + + return React.useCallback( + (id: string) => { + const callableFunction = functions.find((fn) => fn.id === id); + return callableFunction; + }, + [functions] + ); +}; diff --git a/packages/web/src/store/hooks/callableFunctions/useGetFunctionCode.ts b/packages/web/src/store/hooks/callableFunctions/useGetFunctionCode.ts new file mode 100644 index 0000000..e4a8dd1 --- /dev/null +++ b/packages/web/src/store/hooks/callableFunctions/useGetFunctionCode.ts @@ -0,0 +1,13 @@ +import React from "react"; +import { useAppStore } from "../.."; + +export const useGetFunctionCode = () => { + const codes = useAppStore((state) => state.callableFunctionCodes); + + return React.useCallback( + (id: string): string | undefined => { + return codes[id]; + }, + [codes] + ); +}; diff --git a/packages/web/src/store/hooks/callableFunctions/useGetFunctionDisplayName.ts b/packages/web/src/store/hooks/callableFunctions/useGetFunctionDisplayName.ts new file mode 100644 index 0000000..4b632ce --- /dev/null +++ b/packages/web/src/store/hooks/callableFunctions/useGetFunctionDisplayName.ts @@ -0,0 +1,16 @@ +import React from "react"; +import { useAppStore } from "../.."; + +export const useGetFunctionDisplayName = () => { + const displayNames = useAppStore( + (state) => state.callableFunctionDisplayNames + ); + + return React.useCallback( + (id: string): string | undefined => { + const displayName = displayNames[id]; + return displayName; + }, + [displayNames] + ); +}; diff --git a/packages/web/src/store/hooks/callableFunctions/useGetUniqueFunctionDisplayName.ts b/packages/web/src/store/hooks/callableFunctions/useGetUniqueFunctionDisplayName.ts new file mode 100644 index 0000000..27c29cf --- /dev/null +++ b/packages/web/src/store/hooks/callableFunctions/useGetUniqueFunctionDisplayName.ts @@ -0,0 +1,24 @@ +import React from "react"; +import { useAppStore } from "../.."; +import getUniqueString from "../../../utils/getUniqueString"; + +export const useGetUniqueFunctionDisplayName = () => { + const callableFunctionDisplayNames = useAppStore( + (state) => state.callableFunctionDisplayNames + ); + + return React.useCallback( + (displayName: string, extraDisplayNames: string[] = []) => { + const usedDisplayNames = [ + ...Object.values(callableFunctionDisplayNames), + ...extraDisplayNames, + ]; + return getUniqueString( + displayName, + usedDisplayNames, + (str, i) => `${str} (${i})` + ); + }, + [callableFunctionDisplayNames] + ); +}; diff --git a/packages/web/src/store/hooks/callableFunctions/useGetUniqueFunctionName.ts b/packages/web/src/store/hooks/callableFunctions/useGetUniqueFunctionName.ts new file mode 100644 index 0000000..55846c1 --- /dev/null +++ b/packages/web/src/store/hooks/callableFunctions/useGetUniqueFunctionName.ts @@ -0,0 +1,20 @@ +import React from "react"; +import { useAppStore } from "../.."; +import getUniqueString from "../../../utils/getUniqueString"; + +export const useGetUniqueFunctionName = () => { + const callableFunctions = useAppStore((state) => state.callableFunctions); + const names = React.useMemo( + () => + callableFunctions.map((callableFunction) => callableFunction.name), + [callableFunctions] + ); + + return React.useCallback( + (name: string, extraNames: string[] = []) => { + const usedNames = [...names, ...extraNames]; + return getUniqueString(name, usedNames, (n, i) => `${n}${i}`); + }, + [names] + ); +}; diff --git a/packages/web/src/store/hooks/conversations/useActiveConversation.ts b/packages/web/src/store/hooks/conversations/useActiveConversation.ts new file mode 100644 index 0000000..7773cbd --- /dev/null +++ b/packages/web/src/store/hooks/conversations/useActiveConversation.ts @@ -0,0 +1,16 @@ +import React from "react"; +import { shallow } from "zustand/shallow"; +import { useAppStore } from "../.."; + +export const useActiveConversation = () => { + const [conversations, activeConversationId] = useAppStore( + (state) => [state.conversations, state.activeConversationId], + shallow + ); + + return React.useMemo(() => { + return conversations.find( + (conversation) => conversation.id === activeConversationId + ); + }, [activeConversationId, conversations]); +}; diff --git a/packages/web/src/store/hooks/conversations/useGetConversationLastEdit.ts b/packages/web/src/store/hooks/conversations/useGetConversationLastEdit.ts new file mode 100644 index 0000000..7167069 --- /dev/null +++ b/packages/web/src/store/hooks/conversations/useGetConversationLastEdit.ts @@ -0,0 +1,13 @@ +import React from "react"; +import { useAppStore } from "../.."; + +export const useGetConversationLastEdit = () => { + const conversationLastEdits = useAppStore( + (state) => state.conversationLastEdits + ); + + return React.useCallback( + (id: string) => conversationLastEdits.get(id), + [conversationLastEdits] + ); +}; diff --git a/packages/web/src/store/hooks/conversations/useGetConversationName.ts b/packages/web/src/store/hooks/conversations/useGetConversationName.ts new file mode 100644 index 0000000..f5399fd --- /dev/null +++ b/packages/web/src/store/hooks/conversations/useGetConversationName.ts @@ -0,0 +1,11 @@ +import React from "react"; +import { useAppStore } from "../.."; + +export const useGetConversationName = () => { + const conversationNames = useAppStore((state) => state.conversationNames); + + return React.useCallback( + (id: string) => conversationNames.get(id), + [conversationNames] + ); +}; diff --git a/packages/web/src/store/index.ts b/packages/web/src/store/index.ts index 94aae0d..264206b 100644 --- a/packages/web/src/store/index.ts +++ b/packages/web/src/store/index.ts @@ -58,7 +58,7 @@ export type AppStateSlice = StateCreator< [ ["zustand/persist", AppPersistedState], ["zustand/immer", never], - ["zustand/devtools", never] + ["zustand/devtools", never], ], [], T diff --git a/packages/web/src/store/persist/migrateOldData.ts b/packages/web/src/store/persist/migrateOldData.ts index 891c2b0..754157a 100644 --- a/packages/web/src/store/persist/migrateOldData.ts +++ b/packages/web/src/store/persist/migrateOldData.ts @@ -1,13 +1,5 @@ import { notifications } from "@mantine/notifications"; import { CallableFunction, Conversation } from "gpt-turbo"; -import { addConversation } from "../actions/conversations/addConversation"; -import { setConversationName } from "../actions/conversations/setConversationName"; -import { setConversationLastEdit } from "../actions/conversations/setConversationLastEdit"; -import { addCallableFunction } from "../actions/callableFunctions/addCallableFunction"; -import { saveContext } from "../actions/savedContexts/saveContext"; -import { savePrompt } from "../actions/savedPrompts/savePrompt"; -import { setDefaultSettings } from "../actions/defaultConversationSettings/setDefaultSettings"; -import { addPersistedConversationId } from "../actions/persistence/addPersistedConversationId"; const notify = ( title: string, @@ -28,6 +20,31 @@ const notify = ( // TODO: Remove this after a while. This is to migrate users from the old persistence system to the new one. // Not EVERYTHING is migrated, but the most important things are. export const migrateOldData = async () => { + // Wait a second to make sure the store is initialized + await new Promise((resolve) => setTimeout(resolve, 1000)); + const { addConversation } = await import( + "../actions/conversations/addConversation" + ); + const { setConversationName } = await import( + "../actions/conversations/setConversationName" + ); + const { setConversationLastEdit } = await import( + "../actions/conversations/setConversationLastEdit" + ); + const { addCallableFunction } = await import( + "../actions/callableFunctions/addCallableFunction" + ); + const { saveContext } = await import( + "../actions/savedContexts/saveContext" + ); + const { savePrompt } = await import("../actions/savedPrompts/savePrompt"); + const { setDefaultSettings } = await import( + "../actions/defaultConversationSettings/setDefaultSettings" + ); + const { addPersistedConversationId } = await import( + "../actions/persistence/addPersistedConversationId" + ); + const oldPersistence = localStorage.getItem("gpt-turbo-persistence"); const oldSettings = localStorage.getItem("gpt-turbo-settings"); diff --git a/packages/web/src/store/persist/onStoreRehydrate.tsx b/packages/web/src/store/persist/onStoreRehydrate.tsx index 48e2772..2fcd7af 100644 --- a/packages/web/src/store/persist/onStoreRehydrate.tsx +++ b/packages/web/src/store/persist/onStoreRehydrate.tsx @@ -1,7 +1,7 @@ import { modals } from "@mantine/modals"; import { AppState } from ".."; import { Text } from "@mantine/core"; -import StorageLoadError from "../../components/StorageLoadError"; +import StorageLoadError from "../../components/error-handling/StorageLoadError"; import { STORAGE_PERSISTENCE_KEY } from "../../config/constants"; import { StoreMigrationError } from "./migrations"; import getErrorInfo from "../../utils/getErrorInfo"; diff --git a/packages/web/src/store/persist/partializeStore.ts b/packages/web/src/store/persist/partializeStore.ts index c668696..e3b3f7c 100644 --- a/packages/web/src/store/persist/partializeStore.ts +++ b/packages/web/src/store/persist/partializeStore.ts @@ -1,4 +1,5 @@ import { AppPersistedState, AppState } from ".."; +import { DEFAULT_CONVERSATION_NAME } from "../../config/constants"; import { persistenceSchema } from "../../entities/persistence"; export const partializeStore = (state: AppState): AppPersistedState => { @@ -32,7 +33,9 @@ export const partializeStore = (state: AppState): AppPersistedState => { .map((c) => { const lastEdited = state.conversationLastEdits.get(c.id) ?? Date.now(); - const name = state.conversationNames.get(c.id) ?? "New Chat"; + const name = + state.conversationNames.get(c.id) ?? + DEFAULT_CONVERSATION_NAME; return { ...c.toJSON(), lastEdited, diff --git a/packages/web/src/store/persist/triggerPersist.ts b/packages/web/src/store/persist/triggerPersist.ts index 66e7d08..47c948c 100644 --- a/packages/web/src/store/persist/triggerPersist.ts +++ b/packages/web/src/store/persist/triggerPersist.ts @@ -1,14 +1,14 @@ -import { useAppStore } from ".."; import { STORAGE_PERSISTENCE_KEY } from "../../config/constants"; +import { createAction } from "../actions/createAction"; import { storeVersion } from "./migrations"; import { partializeStore } from "./partializeStore"; import { storeStorage } from "./storeStorage"; -export const persistStore = () => { - const state = useAppStore.getState(); +export const persistStore = createAction(({ get }) => { + const state = get(); const persistedState = partializeStore(state); storeStorage.setItem(STORAGE_PERSISTENCE_KEY, { state: persistedState, version: storeVersion, }); -}; +}, "persistStore"); diff --git a/packages/web/src/utils/getUniqueString.ts b/packages/web/src/utils/getUniqueString.ts new file mode 100644 index 0000000..fe03ec4 --- /dev/null +++ b/packages/web/src/utils/getUniqueString.ts @@ -0,0 +1,12 @@ +export default ( + str: string, + otherStrs: string[], + transform: (str: string, i: number) => string +) => { + let i = 1; + let uniqueStr = str; + while (otherStrs.some((name) => name === uniqueStr)) { + uniqueStr = transform(str, i++); + } + return uniqueStr; +}; diff --git a/packages/web/vite.config.ts b/packages/web/vite.config.ts index 4a6904b..ace978a 100644 --- a/packages/web/vite.config.ts +++ b/packages/web/vite.config.ts @@ -49,6 +49,41 @@ export default defineConfig({ define: { APP_VERSION: JSON.stringify(process.env.npm_package_version), }, + build: { + rollupOptions: { + output: { + manualChunks: { + "mantine-core": [ + "@mantine/core", + "@mantine/hooks", + "@emotion/react", + ], + "mantine-dropzone": ["@mantine/dropzone"], + "mantine-form": ["@mantine/form"], + "mantine-modals": ["@mantine/modals"], + "mantine-notifications": ["@mantine/notifications"], + "mantine-prism": ["@mantine/prism"], + "mantine-tiptap": [ + "@tiptap/extension-code-block-lowlight", + "@tiptap/extension-link", + "@tiptap/react", + "@tiptap/starter-kit", + "@mantine/tiptap", + "@tabler/icons-react", + "lowlight", + ], + "gpt-turbo": ["gpt-turbo"], + react: ["react"], + "react-dom": ["react-dom"], + "react-icons": ["react-icons"], + "react-router-dom": ["react-router-dom"], + uuid: ["uuid"], + zod: ["zod"], + zustand: ["zustand"], + }, + }, + }, + }, server: { port: 3000, host: true,